W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
RPC-client的部分又分為:
(1)序列化反序列化的部分(上圖中的1、4)
(2)發(fā)送字節(jié)流與接收字節(jié)流的部分(上圖中的2、3)
前一篇文章討論了序列化與范序列化的細(xì)節(jié),這一篇文章將討論發(fā)送字節(jié)流與接收字節(jié)流的部分。
客戶端調(diào)用又分為同步調(diào)用與異步調(diào)用
同步調(diào)用的代碼片段為:
Result = Add(Obj1, Obj2);// 得到Result之前處于阻塞狀態(tài)
異步調(diào)用的代碼片段為:
Add(Obj1, Obj2, callback);// 調(diào)用后直接返回,不等結(jié)果
處理結(jié)果通過回調(diào)得到:
callback(Result){// 得到處理結(jié)果后會(huì)調(diào)用這個(gè)回調(diào)函數(shù)
…上圖中的左邊大框,就代表了調(diào)用方的一個(gè)工作線程。
左邊粉色中框,代表了RPC-client組件。
右邊橙色框,代表了RPC-server。
藍(lán)色兩個(gè)小框,代表了同步RPC-client兩個(gè)核心組件,序列化組件與連接池組件。
白色的流程小框,以及箭頭序號(hào)1-10,代表整個(gè)工作線程的串行執(zhí)行步驟:
1)業(yè)務(wù)代碼發(fā)起RPC調(diào)用,Result=Add(Obj1,Obj2)
2)序列化組件,將對(duì)象調(diào)用序列化成二進(jìn)制字節(jié)流,可理解為一個(gè)待發(fā)送的包packet1
3)通過連接池組件拿到一個(gè)可用的連接connection
4)通過連接connection將包packet1發(fā)送給RPC-server
5)發(fā)送包在網(wǎng)絡(luò)傳輸,發(fā)給RPC-server
6)響應(yīng)包在網(wǎng)絡(luò)傳輸,發(fā)回給RPC-client
7)通過連接connection從RPC-server收取響應(yīng)包packet2
8)通過連接池組件,將conneciont放回連接池
9)序列化組件,將packet2范序列化為Result對(duì)象返回給調(diào)用方
10)業(yè)務(wù)代碼獲取Result結(jié)果,工作線程繼續(xù)往下走
RPC框架需要支持負(fù)載均衡、故障轉(zhuǎn)移、發(fā)送超時(shí),這些特性都是通過連接池組件去實(shí)現(xiàn)的。
連接池組件
【INIT】
和下游RPC-server(一般是一個(gè)集群),建立N個(gè)tcp長連接,即所謂的連接“池”
【getConnection】
從連接“池”中拿一個(gè)連接,加鎖(置一個(gè)標(biāo)志位),返回給調(diào)用方
【putConnection】
將一個(gè)分配出去的連接放回連接“池”中,解鎖(也是置一個(gè)標(biāo)志位)
如何實(shí)現(xiàn)負(fù)載均衡?
回答:連接池中建立了與一個(gè)RPC-server集群的連接,連接池在返回連接的時(shí)候,需要具備隨機(jī)性。
如何實(shí)現(xiàn)故障轉(zhuǎn)移?
回答:連接池中建立了與一個(gè)RPC-server集群的連接,當(dāng)連接池發(fā)現(xiàn)某一個(gè)機(jī)器的連接異常后,需要將這個(gè)機(jī)器的連接排除掉,返回正常的連接,在機(jī)器恢復(fù)后,再將連接加回來。
如何實(shí)現(xiàn)發(fā)送超時(shí)?
回答:因?yàn)槭峭阶枞{(diào)用,拿到一個(gè)連接后,使用帶超時(shí)的send/recv即可實(shí)現(xiàn)帶超時(shí)的發(fā)送和接收。
上圖中左邊的框框,是少量工作線程(少數(shù)幾個(gè)就行了)進(jìn)行調(diào)用與回調(diào)。
中間粉色的框框,代表了RPC-client組件。
右邊橙色框,代表了RPC-server。
藍(lán)色六個(gè)小框,代表了異步RPC-client六個(gè)核心組件:上下文管理器,超時(shí)管理器,序列化組件,下游收發(fā)隊(duì)列,下游收發(fā)線程,連接池組件。
白色的流程小框,以及箭頭序號(hào)1-17,代表整個(gè)工作線程的串行執(zhí)行步驟:
1)業(yè)務(wù)代碼發(fā)起異步RPC調(diào)用,Add(Obj1,Obj2, callback)
2)上下文管理器,將請(qǐng)求,回調(diào),上下文存儲(chǔ)起來
3)序列化組件,將對(duì)象調(diào)用序列化成二進(jìn)制字節(jié)流,可理解為一個(gè)待發(fā)送的包packet1
4)下游收發(fā)隊(duì)列,將報(bào)文放入“待發(fā)送隊(duì)列”,此時(shí)調(diào)用返回,不會(huì)阻塞工作線程
5)下游收發(fā)線程,將報(bào)文從“待發(fā)送隊(duì)列”中取出,通過連接池組件拿到一個(gè)可用的連接connection
6)通過連接connection將包packet1發(fā)送給RPC-server
7)發(fā)送包在網(wǎng)絡(luò)傳輸,發(fā)給RPC-server
8)響應(yīng)包在網(wǎng)絡(luò)傳輸,發(fā)回給RPC-client
9)通過連接connection從RPC-server收取響應(yīng)包packet2
10)下游收發(fā)線程,將報(bào)文放入“已接受隊(duì)列”,通過連接池組件,將conneciont放回連接池
11)下游收發(fā)隊(duì)列里,報(bào)文被取出,此時(shí)回調(diào)將要開始,不會(huì)阻塞工作線程
12)序列化組件,將packet2范序列化為Result對(duì)象
13)上下文管理器,將結(jié)果,回調(diào),上下文取出
14)通過callback回調(diào)業(yè)務(wù)代碼,返回Result結(jié)果,工作線程繼續(xù)往下走
如果請(qǐng)求長時(shí)間不返回,處理流程是:
15)上下文管理器,請(qǐng)求長時(shí)間沒有返回
16)超時(shí)管理器拿到超時(shí)的上下文
17)通過timeout_cb回調(diào)業(yè)務(wù)代碼,工作線程繼續(xù)往下走
上下文管理器
為什么需要上下文管理器?
回答:由于請(qǐng)求包的發(fā)送,響應(yīng)包的回調(diào)都是異步的,甚至不在同一個(gè)工作線程中完成,需要一個(gè)組件來記錄一個(gè)請(qǐng)求的上下文,把請(qǐng)求-響應(yīng)-回調(diào)等一些信息匹配起來。
如何將請(qǐng)求-響應(yīng)-回調(diào)這些信息匹配起來?
這是一個(gè)很有意思的問題,通過一條連接往下游服務(wù)發(fā)送了a,b,c三個(gè)請(qǐng)求包,異步的收到了x,y,z三個(gè)響應(yīng)包:
(1)怎么知道哪個(gè)請(qǐng)求包與哪個(gè)響應(yīng)包對(duì)應(yīng)?
(2)怎么知道哪個(gè)響應(yīng)包與哪個(gè)回調(diào)函數(shù)對(duì)應(yīng)?
回答:這是通過【請(qǐng)求id】來實(shí)現(xiàn)請(qǐng)求-響應(yīng)-回調(diào)的串聯(lián)的。
整個(gè)處理流程如上,通過請(qǐng)求id,上下文管理器來對(duì)應(yīng)請(qǐng)求-響應(yīng)-callback之間的映射關(guān)系:
1)生成請(qǐng)求id
2)生成請(qǐng)求上下文context,上下文中包含發(fā)送時(shí)間time,回調(diào)函數(shù)callback等信息
3)上下文管理器記錄req-id與上下文context的映射關(guān)系,
4)將req-id打在請(qǐng)求包里發(fā)給RPC-server
5)RPC-server將req-id打在響應(yīng)包里返回
6)由響應(yīng)包中的req-id,通過上下文管理器找到原來的上下文context
7)從上下文context中拿到回調(diào)函數(shù)callback
8)callback將Result帶回,推動(dòng)業(yè)務(wù)的進(jìn)一步執(zhí)行
如何實(shí)現(xiàn)負(fù)載均衡,故障轉(zhuǎn)移?
回答:與同步的連接池思路相同。不同在于,同步連接池使用阻塞方式收發(fā),需要與一個(gè)服務(wù)的一個(gè)ip建立多條連接,異步收發(fā),一個(gè)服務(wù)的一個(gè)ip只需要建立少量的連接(例如,一條tcp連接)。
如何實(shí)現(xiàn)超時(shí)發(fā)送與接收?
回答:同步阻塞發(fā)送,可以直接使用帶超時(shí)的send/recv來實(shí)現(xiàn),異步非阻塞的nio的網(wǎng)絡(luò)報(bào)文收發(fā),如何實(shí)現(xiàn)超時(shí)接收呢?(由于連接不會(huì)一直等待回包,那如何知曉超時(shí)呢?)這時(shí),超時(shí)管理器就上場啦。
超時(shí)管理器,用于實(shí)現(xiàn)請(qǐng)求回包超時(shí)回調(diào)處理。
每一個(gè)請(qǐng)求發(fā)送給下游RPC-server,會(huì)在上下文管理器中保存req-id與上下文的信息,上下文中保存了請(qǐng)求很多相關(guān)信息,例如req-id,回包回調(diào),超時(shí)回調(diào),發(fā)送時(shí)間等。
超時(shí)管理器啟動(dòng)timer對(duì)上下文管理器中的context進(jìn)行掃描,看上下文中請(qǐng)求發(fā)送時(shí)間是否過長,如果過長,就不再等待回包,直接超時(shí)回調(diào),推動(dòng)業(yè)務(wù)流程繼續(xù)往下走,并將上下文刪除掉。
如果超時(shí)回調(diào)執(zhí)行后,正常的回包又到達(dá),通過req-id在上下文管理器里找不到上下文,就直接將請(qǐng)求丟棄(因?yàn)橐呀?jīng)超時(shí)處理過了)。
however,異步回調(diào)和同步回調(diào)相比,除了序列化組件和連接池組件,會(huì)多出上下文管理器,超時(shí)管理器,下游收發(fā)隊(duì)列,下游收發(fā)線程等組件,并且對(duì)調(diào)用方的調(diào)用習(xí)慣有影響(同步->回調(diào))。異步回調(diào)能提高系統(tǒng)整體的吞吐量,具體使用哪種方式實(shí)現(xiàn)RPC-client,可以結(jié)合業(yè)務(wù)場景來選取(對(duì)時(shí)延敏感的可以選用同步,對(duì)吞吐量敏感的可以選用異步)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: