1提岔, 先從protobuf開始吧波岛。
protobuf是一個(gè)高效的序列化協(xié)議荐健,protobuf分兩部分暖璧,一部分是用c++編寫的protoc編譯器案怯,用于把proto文件編譯為java/c#/go/c++等語(yǔ)言的支持序列化反序列化的對(duì)象。
protoc -I [proto文件路徑] --java_out [生成的類文件路徑] [proto文件路徑]
生成的java類都繼承自protobuf-java中的類澎办。
以后有時(shí)間分析一下protoc的源碼3凹睢!浮驳!
2悍汛, 如正題,先從源碼中的helloword開始至会。
下面開始分析server啟動(dòng)過(guò)程以及一個(gè)請(qǐng)求的處理過(guò)程离咐。
3, 從類名就可以看出使用的構(gòu)造器設(shè)計(jì)模式奉件,此模式特別適用于配置參數(shù)特別多的類宵蛀。lombok中的@Builder注解非常方便。
從源碼中可以看到县貌,此處通過(guò)jdk中的spi加載ServerProvider术陶,最終加載到的是NettyServerProvider類。NettyServerProvider的builderForPort返回NettyServerBuilder類煤痕。
ServerProvider里的方法不多梧宫,用于注冊(cè)服務(wù),添加攔截器摆碉,初始化server狀態(tài)塘匣。
最后調(diào)用build構(gòu)造出server對(duì)象。構(gòu)造的過(guò)程如下:
ServerImpl維護(hù)著整個(gè)server的狀態(tài)巷帝,最關(guān)鍵的屬性如下:
1忌卤,ObjectPool<? extends Executor> executorPool:執(zhí)行請(qǐng)求的服務(wù)方法的線程池。
2楞泼,InternalHandlerRegistry registry:服務(wù)方法主注冊(cè)表驰徊,運(yùn)行時(shí)不能修改笤闯。
3,HandlerRegistry fallbackRegistry:fallback注冊(cè)表棍厂,運(yùn)行時(shí)可以動(dòng)態(tài)修改颗味。
4,List<ServerTransportFilter> transportFilters:監(jiān)聽client連接ready和terminal事件勋桶。
5脱衙, ServerInterceptor[] interceptors:服務(wù)攔截器,添加順序和執(zhí)行順序相反例驹,服務(wù)方法執(zhí)行時(shí)以裝飾器設(shè)計(jì)模式嵌入執(zhí)行流程捐韩。
6, InternalServer transportServer:NettyServer鹃锈。
7荤胁, Collection<ServerTransport> transports:accept到的client連接。
8屎债, DecompressorRegistry decompressorRegistry:解碼器注冊(cè)表仅政。
9, CompressorRegistry compressorRegistry:編碼器注冊(cè)表盆驹。
10圆丹, BinaryLogProvider binlogProvider:記錄log。
11躯喇, Channelz channelz:記錄server辫封,channel的狀態(tài)。
12廉丽, CallTracer serverCallTracer:統(tǒng)計(jì)rpc調(diào)用次數(shù)倦微。
4, server構(gòu)造之后開始調(diào)用start方法啟動(dòng)正压。
此處的重點(diǎn)是啟動(dòng)傳輸層時(shí)傳入了ServerListener欣福,用來(lái)client連接建立或終結(jié)時(shí)從傳輸層回調(diào)server。
netty啟動(dòng)過(guò)程焦履,重點(diǎn)是childHandler設(shè)置的處理client請(qǐng)求的pipeline拓劝。
此處的重點(diǎn)是ServerTransportListener,在連接建立和收到client請(qǐng)求數(shù)據(jù)時(shí)從傳輸層回調(diào)server嘉裤。
洗盡鉛華凿将,發(fā)現(xiàn)最終處理client的handler只有NettyServerHandler而已。
從類層次上看以看到NettyServerHandler只是一個(gè)解碼器decoder价脾,具體點(diǎn)就是Http2ConnectionHandler,由此可見笛匙,grpc的傳輸協(xié)議是http2侨把,相比與http1.1效率有很大提升犀变,據(jù)說(shuō)主要在兩方面:利用IO多播使一個(gè)連接可以請(qǐng)求多個(gè)資源,header壓縮秋柄,有空詳細(xì)分析http2協(xié)議获枝。
在NettyServerHandler的構(gòu)造過(guò)程中有一句至關(guān)重要: decoder().frameListener(new FrameListener())。熟悉netty codec機(jī)制的一看就明白骇笔,就是根據(jù)http2協(xié)議省店,每次解析到一幀都會(huì)通知。
重點(diǎn)是onDataRead笨触,onHeadersRead懦傍,onSettingsRead(tcp握手完成)顧名思義。
現(xiàn)在萬(wàn)事具備了芦劣,server已經(jīng)啟動(dòng)粗俱,啟動(dòng)的過(guò)程主要在注冊(cè)各種服務(wù),添加服務(wù)攔截器等虚吟,初始化netty寸认。
當(dāng)有請(qǐng)求到來(lái)時(shí),ByteToMessageDecoder會(huì)按http2協(xié)議解析幀串慰,當(dāng)收到http header時(shí)偏塞,
此處最關(guān)鍵的時(shí)構(gòu)造了NettyServerStream,此類的作用是一個(gè)請(qǐng)求完成后把響應(yīng)信息返回給client邦鲫,最重要的屬性是WriteQueue writeQueue灸叼,響應(yīng)信息的隊(duì)列,緩存響應(yīng)信息掂碱,等全部處理完怜姿,調(diào)用flush把所有響應(yīng)信息返回給client。
另一個(gè)重點(diǎn)是通過(guò)listener回調(diào)通知server已經(jīng)收到了數(shù)據(jù)包疼燥,第一個(gè)數(shù)據(jù)包永遠(yuǎn)都是header沧卢。header都有啥?
最關(guān)鍵的是method醉者,服務(wù)方法的全限定名但狭。
現(xiàn)在的流程從傳輸層轉(zhuǎn)到了server。
此處設(shè)置了一個(gè)非常關(guān)鍵的JumpToApplicationThreadServerStreamListener撬即,當(dāng)IO線程收齊http body之后回調(diào)立磁。
很明顯,找到請(qǐng)求的服務(wù)剥槐,調(diào)用它唱歧。很神奇,只收到header就開始調(diào)用服務(wù)!B馈几于!netty的io是全異步的,調(diào)用的過(guò)程中已經(jīng)在接收http body啦沿后。
裝飾器設(shè)計(jì)模式的應(yīng)用沿彭,套了一層又一層,此處很容易明白為啥服務(wù)攔截器添加順序和執(zhí)行順序相反啦尖滚。
終于開始調(diào)用方法了喉刘,根據(jù)請(qǐng)求的方法的類型調(diào)用對(duì)應(yīng)的ServerCallHandler。
以最簡(jiǎn)單又最常見的UNARY為例吧漆弄,一個(gè)請(qǐng)求跟隨一個(gè)響應(yīng)睦裳。
此處的call.request(2)至關(guān)重要,2表示應(yīng)該收到2個(gè)幀置逻,注釋里解釋里原因推沸。
當(dāng)收到http body時(shí),通過(guò)一大串listener的通知券坞,最后到達(dá)onMessage鬓催,此時(shí)的request已經(jīng)經(jīng)過(guò)里protobuf的反序列化,轉(zhuǎn)成里請(qǐng)求對(duì)象恨锚。
根據(jù)tcp協(xié)議宇驾,client發(fā)送完請(qǐng)求后,如果不是長(zhǎng)連接猴伶,會(huì)關(guān)閉輸入端课舍,等待響應(yīng)。一旦輸入端關(guān)閉他挎,就知道所有請(qǐng)求都發(fā)送完啦筝尾,終于可以調(diào)用服務(wù)方法啦。
那這個(gè)UnaryServerCallListener是怎么被調(diào)用呢办桨。
就在上面的call.request(2)這句啦筹淫,
一直循環(huán)直到讀完所有數(shù)據(jù),但是這個(gè)循環(huán)本身并不直接操作IO呢撞,它只是把CompositeReadableBuffer unprocessed里面的內(nèi)容收起起來(lái)判斷是否讀到了需要的數(shù)據(jù)损姜。
毫無(wú)疑問(wèn),unprocessed里的內(nèi)容是IO線程讀出來(lái)的殊霞。
此時(shí)FrameListener收到header之后正忙著收http body摧阅。
通知listener收到了新的數(shù)據(jù)包
把收到的數(shù)據(jù)都放到CompositeReadableBuffer unprocessed緩存起來(lái)。
一個(gè)在監(jiān)聽?zhēng)炼祝缓笞x數(shù)據(jù)放到unprocessed里緩存棒卷,一個(gè)是循環(huán)匯總unprocessed里的數(shù)據(jù),終于交織在一起啦。
等http body讀完之后娇跟,開始通知之前建立的一大串listener岩齿。中間會(huì)經(jīng)過(guò)protobuf反序列化的過(guò)程,最終到達(dá)UnaryServerCallListener.onMessage苞俘。
然后等待netty收到輸入關(guān)閉事件,通過(guò)一串listener龄章,最終到達(dá)UnaryServerCallListener.onHalfClose事件吃谣。然后真正的到了執(zhí)行服務(wù)方法的時(shí)候,method.invoke(request, responseObserver)做裙;服務(wù)方法執(zhí)行完岗憋,得到的結(jié)果會(huì)回調(diào)responseObserver.onNext方法,
最后觸發(fā)channel的flush锚贱,返回響應(yīng)信息仔戈。
5, 再縷一下整個(gè)請(qǐng)求處理流程拧廊。
1监徘,server在啟動(dòng)netty時(shí)傳入了ServerListenerImpl,用于接收client連接建立的回調(diào)通知吧碾。
2凰盔, netty通知server client連接建立時(shí),server返回給netty的是ServerTransportListenerImpl倦春,用于netty在收到http請(qǐng)求幀時(shí)回調(diào)通知server户敬。
3,netty收到http header時(shí)創(chuàng)建NettyServerStream睁本,然后通過(guò)回調(diào)通知server尿庐,server根據(jù)http header里的method屬性查詢請(qǐng)求的服務(wù)方法,給NettyServerStream又設(shè)置一個(gè)JumpToApplicationThreadServerStreamListener呢堰,用于當(dāng)收到http body時(shí)回調(diào)抄瑟。
4, 收到http header時(shí)開始startCall暮胧,UnaryServerCallHandler.startCall時(shí)通過(guò)調(diào)用ServerCall.request方法锐借,進(jìn)入MessageDeframer的request方法,然后一直循環(huán)檢查是否收到了指定幀數(shù)的http body往衷,UnaryServerCallHandler.startCall返回UnaryServerCallListener钞翔,返回的listener又被wrap到ServerStreamListenerImpl里,wrap之后又被set到JumpToApplicationThreadServerStreamListener里席舍。
5布轿,整個(gè)listener鏈已經(jīng)設(shè)置好了,就等著netty收到http body啦。
6汰扭, netty最終收齊了http body稠肘。然后第4步中startCall里那個(gè)循環(huán)檢測(cè)到http body收齊了,結(jié)束循環(huán)萝毛。
7项阴, 然后開始沿著listener鏈通知下去,請(qǐng)求信息在protobuf反序列化之后最終到達(dá)UnaryServerCallListener.onMessage保存起來(lái)
8笆包, 等待netty收到輸入端關(guān)閉的事件环揽,然后執(zhí)行服務(wù)方法。
9庵佣, 服務(wù)方法執(zhí)行完歉胶,響應(yīng)信息通過(guò)ServerCallStreamObserverImpl.onNext方法protobuf序列化之后椿争,觸發(fā)netty channel的flush事件灭返,返回響應(yīng)給client略贮。
從請(qǐng)求到響應(yīng)的整個(gè)流程結(jié)束I呷0遄场摔认!