gRPC客户端创建和调用原理解析
1. gRPC客戶端創(chuàng)建流程
1.1. 背景
gRPC是在HTTP/2之上實現(xiàn)的RPC框架,HTTP/2是第7層(應(yīng)用層)協(xié)議,它運行在TCP(第4層 - 傳輸層)協(xié)議之上,相比于傳統(tǒng)的REST/JSON機制有諸多的優(yōu)點:
- 基于HTTP/2之上的二進制協(xié)議(Protobuf序列化機制)
- 一個連接上可以多路復(fù)用,并發(fā)處理多個請求和響應(yīng)
- 多種語言的類庫實現(xiàn)
- 服務(wù)定義文件和自動代碼生成(.proto文件和Protobuf編譯工具)
此外,gRPC還提供了很多擴展點,用于對框架進行功能定制和擴展,例如,通過開放負載均衡接口可以無縫的與第三方組件進行集成對接(Zookeeper、域名解析服務(wù)、SLB服務(wù)等)。
一個完整的RPC調(diào)用流程示例如下:
(點擊放大圖像)
圖1-1 通用RPC調(diào)用流程
gRPC的RPC調(diào)用與上述流程相似,下面我們一起學(xué)習(xí)下gRPC的客戶端創(chuàng)建和服務(wù)調(diào)用流程。
1.2. 業(yè)務(wù)代碼示例
以gRPC入門級的helloworld Demo為例,客戶端發(fā)起RPC調(diào)用的代碼主要包括如下幾部分:
1) 根據(jù)hostname和port創(chuàng)建ManagedChannelImpl
2) 根據(jù)helloworld.proto文件生成的GreeterGrpc創(chuàng)建客戶端Stub,用來發(fā)起RPC調(diào)用
3) 使用客戶端Stub(GreeterBlockingStub)發(fā)起RPC調(diào)用,獲取響應(yīng)。
相關(guān)示例代碼如下所示:
(點擊放大圖像)
1.3. RPC調(diào)用流程
gRPC的客戶端調(diào)用主要包括基于Netty的HTTP/2客戶端創(chuàng)建、客戶端負載均衡、請求消息的發(fā)送和響應(yīng)接收處理四個流程。
gRPC的客戶端調(diào)用總體流程如下圖所示:
(點擊放大圖像)
圖1-2 gRPC總體調(diào)用流程
gRPC的客戶端調(diào)用流程如下:
1) 客戶端Stub(GreeterBlockingStub)調(diào)用sayHello(request),發(fā)起RPC調(diào)用
2) 通過DnsNameResolver進行域名解析,獲取服務(wù)端的地址信息(列表),隨后使用默認的LoadBalancer策略,選擇一個具體的gRPC服務(wù)端實例
3) 如果與路由選中的服務(wù)端之間沒有可用的連接,則創(chuàng)建NettyClientTransport和NettyClientHandler,發(fā)起HTTP/2連接
4) 對請求消息使用PB(Protobuf)做序列化,通過HTTP/2 Stream發(fā)送給gRPC服務(wù)端
5) 接收到服務(wù)端響應(yīng)之后,使用PB(Protobuf)做反序列化
6) 回調(diào)GrpcFuture的set(Response)方法,喚醒阻塞的客戶端調(diào)用線程,獲取RPC響應(yīng)
需要指出的是,客戶端同步阻塞RPC調(diào)用阻塞的是調(diào)用方線程(通常是業(yè)務(wù)線程),底層Transport的I/O線程(Netty的NioEventLoop)仍然是非阻塞的。
ManagedChannel是對Transport層SocketChannel的抽象,Transport層負責(zé)協(xié)議消息的序列化和反序列化,以及協(xié)議消息的發(fā)送和讀取。ManagedChannel將處理后的請求和響應(yīng)傳遞給與之相關(guān)聯(lián)的ClientCall進行上層處理,同時,ManagedChannel提供了對Channel的生命周期管理(鏈路創(chuàng)建、空閑、關(guān)閉等)。
ManagedChannel提供了接口式的切面ClientInterceptor,它可以攔截RPC客戶端調(diào)用,注入擴展點,以及功能定制,方便框架的使用者對gRPC進行功能擴展。
ManagedChannel的主要實現(xiàn)類ManagedChannelImpl創(chuàng)建流程如下:
(點擊放大圖像)
圖1-3 ManagedChannelImpl創(chuàng)建流程
流程關(guān)鍵技術(shù)點解讀:
ManagedChannel實例構(gòu)造完成之后,即可創(chuàng)建ClientCall,發(fā)起RPC調(diào)用。
完成ManagedChannelImpl創(chuàng)建之后,由ManagedChannelImpl發(fā)起創(chuàng)建一個新的ClientCall實例。ClientCall的用途是業(yè)務(wù)應(yīng)用層的消息調(diào)度和處理,它的典型用法如下:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__ call = channel.newCall(unaryMethod, callOptions);call.start(listener, headers);call.sendMessage(message);call.halfClose();call.request(1);// wait for listener.onMessage()__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__ClientCall實例的創(chuàng)建流程如下所示:
(點擊放大圖像)
圖1-4 ClientCallImpl創(chuàng)建流程
流程關(guān)鍵技術(shù)點解讀:
ClientCallImpl實例創(chuàng)建完成之后,就可以調(diào)用ClientTransport,創(chuàng)建HTTP/2 Client,向gRPC服務(wù)端發(fā)起遠程服務(wù)調(diào)用。
gRPC客戶端底層基于Netty4.1的HTTP/2協(xié)議棧框架構(gòu)建,以便可以使用HTTP/2協(xié)議來承載RPC消息,在滿足標(biāo)準(zhǔn)化規(guī)范的前提下,提升通信性能。
gRPC HTTP/2協(xié)議棧(客戶端)的關(guān)鍵實現(xiàn)是NettyClientTransport和NettyClientHandler,客戶端初始化流程如下所示:
(點擊放大圖像)
圖1-5 HTTP/2 Client創(chuàng)建流程
流程關(guān)鍵技術(shù)點解讀:
1.NettyClientHandler的創(chuàng)建:級聯(lián)創(chuàng)建Netty的Http2FrameReader、Http2FrameWriter和Http2Connection,用于構(gòu)建基于Netty的gRPC HTTP/2客戶端協(xié)議棧。
2.HTTP/2 Client啟動:仍然基于Netty的Bootstrap來初始化并啟動客戶端,但是有兩個細節(jié)需要注意:
- NettyClientHandler(實際被包裝成ProtocolNegotiator.Handler,用于HTTP/2的握手協(xié)商)創(chuàng)建之后,不是由傳統(tǒng)的ChannelInitializer在初始化Channel時將NettyClientHandler加入到pipeline中,而是直接通過Bootstrap的handler方法直接加入到pipeline中,以便可以立即接收發(fā)送任務(wù)。
- 客戶端使用的work線程組并非通常意義的EventLoopGroup,而是一個EventLoop:即HTTP/2客戶端使用的work線程并非一組線程(默認線程數(shù)為CPU內(nèi)核 * 2),而是一個EventLoop線程。這個其實也很容易理解,一個NioEventLoop線程可以同時處理多個HTTP/2客戶端連接,它是多路復(fù)用的,對于單個HTTP/2客戶端,如果默認獨占一個work線程組,將造成極大的資源浪費,同時也可能會導(dǎo)致句柄溢出(并發(fā)啟動大量HTTP/2客戶端)。
3. WriteQueue創(chuàng)建:Netty的NioSocketChannel初始化并向Selector注冊之后(發(fā)起HTTP連接之前),立即由NettyClientHandler創(chuàng)建WriteQueue,用于接收并處理gRPC內(nèi)部的各種Command,例如鏈路關(guān)閉指令、發(fā)送Frame指令、發(fā)送Ping指令等。
HTTP/2 Client創(chuàng)建完成之后,即可由客戶端根據(jù)協(xié)商策略發(fā)起HTTP/2連接。如果連接創(chuàng)建成功,后續(xù)即可復(fù)用該HTTP/2連接,進行RPC調(diào)用。
HTTP/2在TCP連接之初通過協(xié)商的方式進行通信,只有協(xié)商成功,才能進行后續(xù)的業(yè)務(wù)層數(shù)據(jù)發(fā)送和接收。
HTTP/2的版本標(biāo)識分為兩類:
- 基于TLS之上構(gòu)架的HTTP/2, 即HTTPS,使用h2表示(ALPN):0x68與0x32
- 直接在TCP之上構(gòu)建的HTTP/2,即HTTP,使用h2c表示
HTTP/2連接創(chuàng)建,分為兩種:通過協(xié)商升級協(xié)議方式和直接連接方式。
假如不知道服務(wù)端是否支持HTTP/2,可以先使用HTTP/1.1進行協(xié)商,客戶端發(fā)送協(xié)商請求消息(只含消息頭),報文示例如下:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__GET / HTTP/1.1 Host: 127.0.0.1 Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__服務(wù)端接收到協(xié)商請求之后,如果不支持HTTP/2,則直接按照HTTP/1.1響應(yīng)返回,雙方通過HTTP/1.1進行通信,報文示例如下:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__HTTP/1.1 200 OK Content-Length: 28 Content-Type: text/cssbody...__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__如果服務(wù)端支持HTTP/2,則協(xié)商成功,返回101結(jié)果碼,通知客戶端一起升級到HTTP/2進行通信,示例報文如下:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c[ HTTP/2 connection...__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__101響應(yīng)之后,服務(wù)需要發(fā)送SETTINGS幀作為連接序言,客戶端接收到101響應(yīng)之后,也必須發(fā)送一個序言作為回應(yīng),示例如下:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n SETTINGS幀__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__客戶端序言發(fā)送完成之后,可以不需要等待服務(wù)端的SETTINGS幀,而直接發(fā)送業(yè)務(wù)請求Frame。
假如客戶端和服務(wù)端已經(jīng)約定使用HTTP/2,則可以免去101協(xié)商和切換流程,直接發(fā)起HTTP/2連接,具體流程如下所示:
(點擊放大圖像)
圖1-6 HTTP/2 直接連接過程
幾個關(guān)鍵點:
- 如果已經(jīng)明確知道服務(wù)端支持HTTP/2,則可免去通過HTTP/1.1 101協(xié)議切換方式進行升級。TCP連接建立之后即可發(fā)送序言,否則只能在接收到服務(wù)端101響應(yīng)之后發(fā)送序言
- 針對一個連接,服務(wù)端第一個要發(fā)送的幀必須是SETTINGS幀,連接序言所包含的SETTINGS幀可以為空
- 客戶端可以在發(fā)送完序言之后發(fā)送應(yīng)用幀數(shù)據(jù),不用等待來自服務(wù)器端的序言SETTINGS幀
gRPC支持三種Protocol Negotiator策略:
- PlaintextNegotiator:明確服務(wù)端支持HTTP/2,采用HTTP直接連接的方式與服務(wù)端建立HTTP/2連接,省去101協(xié)議切換過程。
- PlaintextUpgradeNegotiator:不清楚服務(wù)端是否支持HTTP/2,采用HTTP/1.1協(xié)商模式切換升級到HTTP/2。
- TlsNegotiator:在TLS之上構(gòu)建HTTP/2,協(xié)商采用ALPN擴展協(xié)議,以”h2”作為協(xié)議標(biāo)識符。
下面我們以PlaintextNegotiator為例,了解下基于Netty的HTTP/2連接創(chuàng)建流程:
(點擊放大圖像)
圖1-7 基于Netty的HTTP/2直連流程
總體上看,RPC的負載均衡策略有兩大類:
- 服務(wù)端負載均衡(例如代理模式、外部負載均衡服務(wù))
- 客戶端負載均衡(內(nèi)置負載均衡策略和算法,客戶端實現(xiàn))
外部負載均衡模式如下所示:
(點擊放大圖像)
圖1-8 代理負載均衡模式示意圖
以代理LB模式為例:RPC客戶端向負載均衡代理發(fā)送請求,負載均衡代理按照指定的路由策略,將請求消息轉(zhuǎn)發(fā)到后端可用的服務(wù)實例上。負載均衡代理負責(zé)維護后端可用的服務(wù)列表,如果發(fā)現(xiàn)某個服務(wù)不可用,則將其剔除出路由表。
代理LB模式的優(yōu)點是客戶端不需要實現(xiàn)負載均衡策略算法,也不需要維護后端的服務(wù)列表信息,不直接跟后端的服務(wù)進行通信,在做網(wǎng)絡(luò)安全邊界隔離時,非常實用。例如通過Ngix做L7層負載均衡,將互聯(lián)網(wǎng)前端的流量安全的接入到后端服務(wù)中。
代理LB模式通常支持L4(Transport)和L7(Application)層負載均衡,兩者各有優(yōu)缺點,可以根據(jù)RPC的協(xié)議特點靈活選擇。L4/L7層負載均衡對應(yīng)場景如下:
客戶端負載均衡策略由客戶端內(nèi)置負載均衡能力,通過靜態(tài)配置、域名解析服務(wù)(例如DNS服務(wù))、訂閱發(fā)布(例如Zookeeper服務(wù)注冊中心)等方式獲取RPC服務(wù)端地址列表,并將地址列表緩存到客戶端內(nèi)存中。每次RPC調(diào)用時,根據(jù)客戶端配置的負載均衡策略由負載均衡算法從緩存的服務(wù)地址列表中選擇一個服務(wù)實例,發(fā)起RPC調(diào)用。
客戶端負載均衡策略工作原理示例如下:
(點擊放大圖像)
圖1-9 客戶端負載均衡策略示意圖
gRPC默認采用客戶端負載均衡策略,同時提供了擴展機制,使用者通過自定義實現(xiàn)NameResolver和LoadBalancer,即可覆蓋gRPC默認的負載均衡策略,實現(xiàn)自定義路由策略的擴展。
gRPC提供的負載均衡策略實現(xiàn)類如下所示:
- PickFirstBalancer:無負載均衡能力,即使有多個服務(wù)端地址可用,也只選擇第一個地址。
- RoundRobinLoadBalancer:“RoundRobin”負載均衡策略。
gRPC負載均衡流程如下所示:
(點擊放大圖像)
圖1-10 gRPC客戶端負載均衡流程圖
流程關(guān)鍵技術(shù)點解讀:
1.負載均衡功能模塊的輸入是客戶端指定的hostName、需要調(diào)用的接口名和方法名等參數(shù),輸出是執(zhí)行負載均衡算法后獲得的NettyClientTransport。通過NettyClientTransport可以創(chuàng)建基于Netty HTTP/2的gRPC客戶端,發(fā)起RPC調(diào)用。
2.gRPC系統(tǒng)默認提供的是DnsNameResolver,它通過InetAddress.getAllByName(host)獲取指定host的IP地址列表(本地DNS服務(wù))。
對于擴展者而言,可以繼承NameResolver實現(xiàn)自定義的地址解析服務(wù),例如使用Zookeeper替換DnsNameResolver,把Zookeeper作為動態(tài)的服務(wù)地址配置中心,它的偽代碼示例如下:
第一步:繼承NameResolver,實現(xiàn)start(Listener listener)方法:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__void start(Listener listener) {//獲取ZooKeeper地址,并連接//創(chuàng)建Watcher,并實現(xiàn)process(WatchedEvent event),監(jiān)聽地址變更//根據(jù)接口名和方法名,調(diào)用getChildren方法,獲取發(fā)布該服務(wù)的地址列表 //將地址列表加到List中 // 調(diào)用NameResolver.Listener.onAddresses(),通知地址解析完成__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__第二步:創(chuàng)建ManagedChannelBuilder時,指定Target的地址為Zookeeper服務(wù)端地址,同時設(shè)置nameResolver為Zookeeper NameResolver,示例代碼如下所示:
__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__ this(ManagedChannelBuilder.forTarget(zookeeperAddr).loadBalancerFactory(RoundRobinLoadBalancerFactory.getInstance()).nameResolverFactory(new ZookeeperNameResolverProvider()).usePlaintext(false));__Fri Sep 15 2017 09:46:36 GMT+0800 (CST)____Fri Sep 15 2017 09:46:36 GMT+0800 (CST)__3. LoadBalancer負責(zé)從nameResolver中解析獲得的服務(wù)端URL中按照指定路由策略,選擇一個目標(biāo)服務(wù)端地址,并創(chuàng)建ClientTransport。同樣,可以通過覆蓋handleResolvedAddressGroups實現(xiàn)自定義負載均衡策略。
通過LoadBalancer + NameResolver,可以實現(xiàn)靈活的負載均衡策略擴展。例如基于Zookeeper、etcd的分布式配置服務(wù)中心方案。
gRPC默認基于Netty HTTP/2 + PB進行RPC調(diào)用,請求消息發(fā)送流程如下所示:
(點擊放大圖像)
圖1-11 gRPC請求消息發(fā)送流程圖
流程關(guān)鍵技術(shù)點解讀:
gRPC客戶端響應(yīng)消息的接收入口是NettyClientHandler,它的處理流程如下所示:
(點擊放大圖像)
圖1-12 gRPC響應(yīng)消息接收流程圖
流程關(guān)鍵技術(shù)點解讀:
2. 客戶端源碼分析
gRPC客戶端調(diào)用原理并不復(fù)雜,但是代碼卻相對比較繁雜。下面圍繞關(guān)鍵的類庫,對主要功能點進行源碼分析。
2.1. NettyClientTransport功能和源碼分析
NettyClientTransport的主要功能如下:
- 通過start(Listener transportListener) 創(chuàng)建HTTP/2 Client,并連接gRPC服務(wù)端
- 通過newStream(MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions) 創(chuàng)建ClientStream
- 通過shutdown() 關(guān)閉底層的HTTP/2連接
以啟動HTTP/2客戶端為例進行講解:
(點擊放大圖像)
根據(jù)啟動時配置的HTTP/2協(xié)商策略,以NettyClientHandler為參數(shù)創(chuàng)建ProtocolNegotiator.Handler。
創(chuàng)建Bootstrap,并設(shè)置EventLoopGroup,需要指出的是,此處并沒有使用EventLoopGroup,而是它的一種實現(xiàn)類EventLoop,原因在前文中已經(jīng)說明,相關(guān)代碼示例如下:
(點擊放大圖像)
創(chuàng)建WriteQueue并設(shè)置到NettyClientHandler中,用于接收內(nèi)部的各種QueuedCommand,初始化完成之后,發(fā)起HTTP/2連接,代碼如下:
(點擊放大圖像)
2.2. NettyClientHandler功能和源碼分析
NettyClientHandler繼承自Netty的Http2ConnectionHandler,是gRPC接收和發(fā)送HTTP/2消息的關(guān)鍵實現(xiàn)類,也是gRPC和Netty的交互橋梁,它的主要功能如下所示:
- 發(fā)送各種協(xié)議消息給gRPC服務(wù)端
- 接收gRPC服務(wù)端返回的應(yīng)答消息頭、消息體和其它協(xié)議消息
- 處理HTTP/2協(xié)議相關(guān)的指令,例如StreamError、ConnectionError等。
協(xié)議消息的發(fā)送:無論是業(yè)務(wù)請求消息,還是協(xié)議指令消息,都統(tǒng)一封裝成QueuedCommand,由NettyClientHandler攔截并處理,相關(guān)代碼如下所示:
(點擊放大圖像)
協(xié)議消息的接收:NettyClientHandler通過向Http2ConnectionDecoder注冊FrameListener來監(jiān)聽RPC響應(yīng)消息和協(xié)議指令消息,相關(guān)接口如下:
(點擊放大圖像)
FrameListener回調(diào)NettyClientHandler的相關(guān)方法,實現(xiàn)協(xié)議消息的接收和處理:
(點擊放大圖像)
需要指出的是,NettyClientHandler并沒有實現(xiàn)所有的回調(diào)接口,對于需要特殊處理的幾個方法進行了重載,例如onDataRead和onHeadersRead。
2.3. ProtocolNegotiator功能和源碼分析
ProtocolNegotiator用于HTTP/2連接創(chuàng)建的協(xié)商,gRPC支持三種策略并有三個實現(xiàn)子類:
(點擊放大圖像)
gRPC的ProtocolNegotiator實現(xiàn)類完全遵循HTTP/2相關(guān)規(guī)范,以PlaintextUpgradeNegotiator為例,通過設(shè)置Http2ClientUpgradeCodec,用于101協(xié)商和協(xié)議升級,相關(guān)代碼如下所示:
(點擊放大圖像)
2.4. LoadBalancer功能和源碼分析
LoadBalancer負責(zé)客戶端負載均衡,它是個抽象類,gRPC框架的使用者可以通過繼承的方式進行擴展。
gRPC當(dāng)前已經(jīng)支持PickFirstBalancer和RoundRobinLoadBalancer兩種負載均衡策略,未來不排除會提供更多的策略。
以RoundRobinLoadBalancer為例,它的工作原理如下:根據(jù)PickSubchannelArgs來選擇一個Subchannel:
(點擊放大圖像)
再看下Subchannel的選擇算法:
(點擊放大圖像)
即通過順序的方式從服務(wù)端列表中獲取一個Subchannel。
如果用戶需要定制負載均衡策略,則可以在RPC調(diào)用時,使用如下代碼:
(點擊放大圖像)
2.5. ClientCalls功能和源碼分析
ClientCalls提供了各種RPC調(diào)用方式,包括同步、異步、Streaming和Unary方式等,相關(guān)方法如下所示:
(點擊放大圖像)
下面一起看下RPC請求消息的發(fā)送和應(yīng)答接收相關(guān)代碼。
請求調(diào)用主要有兩步:請求Frame構(gòu)造和Frame發(fā)送,請求Frame構(gòu)造代碼如下所示:
(點擊放大圖像)
使用PB對請求消息做序列化,生成InputStream,構(gòu)造請求Frame:
(點擊放大圖像)
Frame發(fā)送代碼如下所示:
(點擊放大圖像)
NettyClientHandler接收到發(fā)送事件之后,調(diào)用Http2ConnectionEncoder將Frame寫入Netty HTTP/2協(xié)議棧:
(點擊放大圖像)
響應(yīng)消息的接收入口是NettyClientHandler,包括HTTP/2 Header和HTTP/2 DATA Frame兩部分,代碼如下:
(點擊放大圖像)
如果參數(shù)endStream為True,說明Stream已經(jīng)結(jié)束,調(diào)用transportTrailersReceived,通知Listener close,代碼如下所示:
(點擊放大圖像)
讀取到HTTP/2 DATA?Frame之后,調(diào)用MessageDeframer的deliver對Frame進行解析,代碼如下:
(點擊放大圖像)
將Frame 轉(zhuǎn)換成InputStream之后,通知ClientStreamListenerImpl,調(diào)用messageRead(final InputStream message),將InputStream反序列化為響應(yīng)對象,相關(guān)代碼如下所示:
(點擊放大圖像)
當(dāng)接收到endOfStream之后,通知ClientStreamListenerImpl,調(diào)用它的close方法,如下所示:
(點擊放大圖像)
最終調(diào)用UnaryStreamToFuture的onClose方法,set響應(yīng)對象,喚醒阻塞的調(diào)用方線程,完成RPC調(diào)用,代碼如下:
(點擊放大圖像)
https://juejin.im/entry/59bb30f76fb9a00a616f1b73
總結(jié)
以上是生活随笔為你收集整理的gRPC客户端创建和调用原理解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GRPC golang版源码分析之客户端
- 下一篇: gRPC学习记录(一)--概念性知识