grpc一次請(qǐng)求處理的過(guò)程

1提岔, 先從protobuf開始吧波岛。
protobuf是一個(gè)高效的序列化協(xié)議荐健,protobuf分兩部分暖璧,一部分是用c++編寫的protoc編譯器案怯,用于把proto文件編譯為java/c#/go/c++等語(yǔ)言的支持序列化反序列化的對(duì)象。


protoc.png

protoc -I [proto文件路徑] --java_out [生成的類文件路徑] [proto文件路徑]
生成的java類都繼承自protobuf-java中的類澎办。

以后有時(shí)間分析一下protoc的源碼3凹睢!浮驳!

2悍汛, 如正題,先從源碼中的helloword開始至会。


helloword.png

下面開始分析server啟動(dòng)過(guò)程以及一個(gè)請(qǐng)求的處理過(guò)程离咐。
3, 從類名就可以看出使用的構(gòu)造器設(shè)計(jì)模式奉件,此模式特別適用于配置參數(shù)特別多的類宵蛀。lombok中的@Builder注解非常方便。


spi加載ServerProvider.png
spi加載ServerProvider.png

從源碼中可以看到县貌,此處通過(guò)jdk中的spi加載ServerProvider术陶,最終加載到的是NettyServerProvider類。NettyServerProvider的builderForPort返回NettyServerBuilder類煤痕。


ServerProvider類層次結(jié)構(gòu).png

ServerProvider里的方法不多梧宫,用于注冊(cè)服務(wù),添加攔截器摆碉,初始化server狀態(tài)塘匣。
最后調(diào)用build構(gòu)造出server對(duì)象。構(gòu)造的過(guò)程如下:


構(gòu)造server.png
構(gòu)造server.png

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)正压。


startup.png

此處的重點(diǎn)是啟動(dòng)傳輸層時(shí)傳入了ServerListener欣福,用來(lái)client連接建立或終結(jié)時(shí)從傳輸層回調(diào)server。


netty.png

netty啟動(dòng)過(guò)程焦履,重點(diǎn)是childHandler設(shè)置的處理client請(qǐng)求的pipeline拓劝。

設(shè)置childHandler.png

此處的重點(diǎn)是ServerTransportListener,在連接建立和收到client請(qǐng)求數(shù)據(jù)時(shí)從傳輸層回調(diào)server嘉裤。

設(shè)置childHandler.png
設(shè)置childHandler.png

洗盡鉛華凿将,發(fā)現(xiàn)最終處理client的handler只有NettyServerHandler而已。


NettyServerHandler.png

從類層次上看以看到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ì)通知。


Http2FrameListener.png

重點(diǎn)是onDataRead笨触,onHeadersRead懦傍,onSettingsRead(tcp握手完成)顧名思義。
FrameListener.png

現(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í)偏塞,


收到http header.png

此處最關(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都有啥?


header.png

最關(guān)鍵的是method醉者,服務(wù)方法的全限定名但狭。

現(xiàn)在的流程從傳輸層轉(zhuǎn)到了server。


設(shè)置listener.png

此處設(shè)置了一個(gè)非常關(guān)鍵的JumpToApplicationThreadServerStreamListener撬即,當(dāng)IO線程收齊http body之后回調(diào)立磁。

查找方法.png

很明顯,找到請(qǐng)求的服務(wù)剥槐,調(diào)用它唱歧。很神奇,只收到header就開始調(diào)用服務(wù)!B馈几于!netty的io是全異步的,調(diào)用的過(guò)程中已經(jīng)在接收http body啦沿后。

調(diào)用服務(wù).png
調(diào)用服務(wù).png

裝飾器設(shè)計(jì)模式的應(yīng)用沿彭,套了一層又一層,此處很容易明白為啥服務(wù)攔截器添加順序和執(zhí)行順序相反啦尖滚。

終于開始調(diào)用方法了喉刘,根據(jù)請(qǐng)求的方法的類型調(diào)用對(duì)應(yīng)的ServerCallHandler。


服務(wù)方法類型.png

以最簡(jiǎn)單又最常見的UNARY為例吧漆弄,一個(gè)請(qǐng)求跟隨一個(gè)響應(yīng)睦裳。


UnaryServerCallHandler.png

此處的call.request(2)至關(guān)重要,2表示應(yīng)該收到2個(gè)幀置逻,注釋里解釋里原因推沸。

UnaryServerCallListener.png

當(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)這句啦筹淫,


request.png
deliver.png
循環(huán)讀.png

一直循環(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摧阅。


收到http body.png

通知listener收到了新的數(shù)據(jù)包

收到http body.png
收到http body.png

把收到的數(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苞俘。


通知listener.png

然后等待netty收到輸入關(guān)閉事件,通過(guò)一串listener龄章,最終到達(dá)UnaryServerCallListener.onHalfClose事件吃谣。然后真正的到了執(zhí)行服務(wù)方法的時(shí)候,method.invoke(request, responseObserver)做裙;服務(wù)方法執(zhí)行完岗憋,得到的結(jié)果會(huì)回調(diào)responseObserver.onNext方法,


觸發(fā)onNext.png
觸發(fā)onNext.png
protobuf序列化.png
writeFrame.png
響應(yīng)隊(duì)列.png
觸發(fā)channel的flush.png

最后觸發(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遄场摔认!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末原押,一起剝皮案震驚了整個(gè)濱河市峡蟋,隨后出現(xiàn)的幾起案子晶通,更是在濱河造成了極大的恐慌璃氢,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狮辽,死亡現(xiàn)場(chǎng)離奇詭異一也,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)喉脖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門椰苟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人树叽,你說(shuō)我怎么就攤上這事舆蝴。” “怎么了题诵?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵洁仗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我性锭,道長(zhǎng)赠潦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任草冈,我火速辦了婚禮她奥,結(jié)果婚禮上瓮增,老公的妹妹穿的比我還像新娘。我一直安慰自己哩俭,他們只是感情好绷跑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凡资,像睡著了一般砸捏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讳苦,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天带膜,我揣著相機(jī)與錄音,去河邊找鬼鸳谜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛式廷,可吹牛的內(nèi)容都是我干的咐扭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼滑废,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蝗肪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蠕趁,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤薛闪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后俺陋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豁延,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年腊状,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诱咏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缴挖,死狀恐怖袋狞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情映屋,我是刑警寧澤苟鸯,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站棚点,受9級(jí)特大地震影響早处,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乙濒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一陕赃、第九天 我趴在偏房一處隱蔽的房頂上張望卵蛉。 院中可真熱鬧,春花似錦么库、人聲如沸傻丝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)葡缰。三九已至,卻和暖如春忱反,著一層夾襖步出監(jiān)牢的瞬間泛释,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工温算, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怜校,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓注竿,卻偏偏與公主長(zhǎng)得像茄茁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巩割,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容