注:文章中使用的dubbo源碼版本為2.5.4
零、文章目錄
- Dubbo的網(wǎng)絡(luò)分層抽象
- Dubbo如何保證Client端與Server端的連通性
- Dubbo編解碼協(xié)議--解決TCP粘包拆包問(wèn)題
- Dubbo的請(qǐng)求響應(yīng)模式,如何將異步IO變?yōu)橥絉PC
- Dubbo線程模型總結(jié)
一、Dubbo的網(wǎng)絡(luò)分層抽象

Dubbo整體設(shè)計(jì)
上圖為Dubbo整體設(shè)計(jì)的分層抽象。
網(wǎng)絡(luò)通信位于Remoting模塊:
- Remoting 實(shí)現(xiàn)是 Dubbo 協(xié)議的實(shí)現(xiàn),如果你選擇 RMI 協(xié)議,整個(gè) Remoting 都不會(huì)用上;
- Remoting 內(nèi)部再劃為
Transport 傳輸層和Exchange 信息交換層; - Transport 層只負(fù)責(zé)單向消息傳輸,是對(duì) Mina, Netty, Grizzly 的抽象,它也可以擴(kuò)展 UDP 傳輸;
-
Exchange 層是在傳輸層之上封裝了 Request-Response 語(yǔ)義;
Remoting模塊類關(guān)系圖
Transport網(wǎng)絡(luò)傳輸層:
- 抽象 mina 和 netty 為統(tǒng)一接口,以 Message 為中心,擴(kuò)展接口為
Channel,Transporter,Client,Server,Codec; -
Channel為網(wǎng)絡(luò)層通道的抽象,其具備單向消息傳輸?shù)哪芰Γ铱梢越壎ê瞳@取屬性; -
Codec為編解碼協(xié)議的抽象,其定義了encode()和decode()方法,做網(wǎng)絡(luò)byte流和通信消息體的轉(zhuǎn)換; -
Client為網(wǎng)絡(luò)層客戶端的抽象,其繼承了Channel接口,同時(shí)定義了重連方法reconnect; -
Server為網(wǎng)絡(luò)層服務(wù)端的抽象,其具備單向消息傳輸消息的能力,且可以獲取綁定在其上的所有網(wǎng)絡(luò)通道Channel; -
Transporter為網(wǎng)絡(luò)傳輸層入口,提供了創(chuàng)建Server和Client的兩個(gè)核心接口;
Exchange信息交換層:
- 封裝請(qǐng)求響應(yīng)模式,同步轉(zhuǎn)異步。以
Request,Response為中心,擴(kuò)展接口為Exchanger,ExchangeChannel,ExchangeClient,ExchangeServer; -
Request,Response為請(qǐng)求響應(yīng)模式消息體的封裝,RPC請(qǐng)求響應(yīng)信息的承載者; -
ExchangeChannel為信息交換層通道的抽象,其繼承了Channel接口,并添加了具有同步請(qǐng)求響應(yīng)語(yǔ)義的request()方法; -
ExchangeClient為信息交換層客戶端的抽象,繼承了ExchangeChannel接口,具有了發(fā)送同步RPC請(qǐng)求并處理響應(yīng)的能力; -
ExchangeServer為信息交換層服務(wù)端的抽象; -
Exchanger為信息交換層入口,提供了創(chuàng)建ExchangeServer和ExchangeClient兩個(gè)核心接口;
二、Dubbo如何保證Client端與Server端的連通性
Dubbo采用雙向心跳的方式檢測(cè)Client端與Server端的連通性。
2.1 心跳請(qǐng)求的發(fā)送
在信息交換層服務(wù)端實(shí)現(xiàn)類HeaderExchangeServer和客戶端實(shí)現(xiàn)類HeaderExchangeClient中包含一個(gè) 心跳定時(shí)任務(wù)HeartBeatTask:
-
HeaderExchangeServer和HeaderExchangeClient啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè)定時(shí)線程池執(zhí)行心跳定時(shí)任務(wù),關(guān)閉時(shí)會(huì)同時(shí)關(guān)閉該心跳定時(shí)任務(wù); -
HeartBeatTask會(huì)循環(huán)檢測(cè)Client或Server中綁定的網(wǎng)絡(luò)通道Channel,將通道中 最近一次接收或發(fā)送消息 的時(shí)間與當(dāng)前時(shí)刻做比較,如果兩者相隔超過(guò)了一個(gè) 心跳周期 ,則主動(dòng)構(gòu)建并通過(guò)Channel向?qū)Χ税l(fā)送一個(gè) 心跳請(qǐng)求消息;
2.2 心跳請(qǐng)求的接收及處理
在請(qǐng)求接收的Handler處理鏈路中,包含有一個(gè) 心跳消息處理器HeartbeatHandler:
- 對(duì)于所有類型的請(qǐng)求消息,該處理器都會(huì)更新對(duì)應(yīng)通道中 最近一次接收消息 的時(shí)間;
- 對(duì)于心跳請(qǐng)求消息,該處理器接收心跳請(qǐng)求并構(gòu)建對(duì)應(yīng)的心跳響應(yīng)通過(guò)通道
Channel發(fā)送回去;
2.3 心跳超時(shí)的檢測(cè)及處理
在信息交換層服務(wù)端實(shí)現(xiàn)類HeaderExchangeServer和客戶端實(shí)現(xiàn)類HeaderExchangeClient中包含一個(gè) 心跳定時(shí)任務(wù)HeartBeatTask:
-
HeartBeatTask會(huì)循環(huán)檢測(cè)Client或Server中綁定的網(wǎng)絡(luò)通道Channel,當(dāng)發(fā)現(xiàn)通道中 最近一次接收消息 的時(shí)間與當(dāng)前檢測(cè)時(shí)間間隔超過(guò) 心跳超時(shí)時(shí)間 時(shí),會(huì)觸發(fā)心跳超時(shí)邏輯的執(zhí)行; - 對(duì)于服務(wù)端,心跳超時(shí)發(fā)生時(shí),會(huì)調(diào)用
channel.close()主動(dòng)斷連對(duì)應(yīng)通道; - 對(duì)于客戶端,心跳超時(shí)發(fā)生時(shí),會(huì)調(diào)用
client.reconnect()執(zhí)行網(wǎng)絡(luò)通道重連邏輯。如果重連失敗則不做處理等待下次心跳超時(shí)檢測(cè)時(shí)再次觸發(fā)重連邏輯;
三、Dubbo編解碼協(xié)議,解決TCP粘包拆包問(wèn)題

Dubbo協(xié)議頭
- Dubbo在TCP協(xié)議的基礎(chǔ)上添加了自己的消息協(xié)議頭,以進(jìn)行
Request、Response消息的編解碼,解決TCP的粘包拆包問(wèn)題。 - 粘包拆包問(wèn)題的說(shuō)明請(qǐng)見 netty學(xué)習(xí)系列八:拆包器
3.1 請(qǐng)求消息協(xié)議頭說(shuō)明
- 0-1byte:一個(gè)魔數(shù)數(shù)字MAGIC,就是一個(gè)固定的數(shù)字
- 2byte:請(qǐng)求的雙向或單向標(biāo)記
- 3byte:無(wú)
- 4-11byte:請(qǐng)求ID,long類型。異步變同步的全局唯一ID,用來(lái)做consumer和provider的來(lái)回通信標(biāo)記
- 12-15byte:消息體長(zhǎng)度,int類型。也就是消息頭+請(qǐng)求數(shù)據(jù)的長(zhǎng)度
3.2 響應(yīng)消息協(xié)議頭說(shuō)明
- 0-1byte:一個(gè)魔數(shù)數(shù)字MAGIC,就是一個(gè)固定的數(shù)字
- 2byte:序列化組件類型,它用于和客戶端約定序列化編碼號(hào)
- 3byte:它是Response的結(jié)果響應(yīng)碼,例如OK=20
- 4-11byte:請(qǐng)求ID,long類型。異步變同步的全局唯一ID,用來(lái)做consumer和provider的來(lái)回通信標(biāo)記
- 12-15byte:消息體長(zhǎng)度,int類型。也就是消息頭+請(qǐng)求數(shù)據(jù)的長(zhǎng)度
3.3 粘包拆包的解決
基本原理就是不斷從TCP緩沖區(qū)中讀取數(shù)據(jù),并將新讀取到的數(shù)據(jù)向后追加到 本地消息緩存 中,然后進(jìn)行解碼處理:
- 如果當(dāng)前本地消息緩存中不足以拼接成一個(gè)業(yè)務(wù)數(shù)據(jù)包,那就保留數(shù)據(jù),繼續(xù)從tcp緩沖區(qū)中讀取數(shù)據(jù);
- 如果當(dāng)前本地消息緩存中能夠拼接成一個(gè)業(yè)務(wù)數(shù)據(jù)包,那就將對(duì)應(yīng)數(shù)據(jù)解碼成一個(gè)完整的業(yè)務(wù)數(shù)據(jù)包并傳遞給業(yè)務(wù)邏輯處理,本地消息緩存中剩余的多余數(shù)據(jù)仍然保留,以便和下次讀到的數(shù)據(jù)嘗試拼接;
- TCP協(xié)議保證了TCP數(shù)據(jù)報(bào)的 不丟不重不亂序 ,這是Dubbo編解碼方式的前提;
四、Dubbo的請(qǐng)求響應(yīng)模式,如何將異步IO變?yōu)橥絉PC
3.1 發(fā)起RPC調(diào)用請(qǐng)求的業(yè)務(wù)線程,是如何同步阻塞等待直到RPC響應(yīng)返回的?
- 業(yè)務(wù)請(qǐng)求線程調(diào)用
HeaderExchangeClient.request()方法發(fā)送RPC請(qǐng)求消息到網(wǎng)絡(luò),然后直接調(diào)用DefaultFuture.get()方法阻塞等待RPC執(zhí)行結(jié)果; -
get()阻塞等待的本質(zhì):循環(huán)檢測(cè)Response結(jié)果是否被設(shè)置成功,如果不成功使用Condition.await()阻塞直到結(jié)果返回; -
NettyClient接收到RPC響應(yīng)消息時(shí),會(huì)調(diào)用DefaultFuture.received()方法,該方法中觸發(fā)了Condition.signal()通知業(yè)務(wù)請(qǐng)求線程解除阻塞等待狀態(tài);
3.2 對(duì)于全雙工的網(wǎng)絡(luò)通信,在多線程并發(fā)請(qǐng)求響應(yīng)的情況下,如果找到RPC響應(yīng)Response對(duì)應(yīng)的RPC請(qǐng)求Request?
- 對(duì)于不同的服務(wù)消費(fèi)者客戶端,請(qǐng)求響應(yīng)自然與其網(wǎng)絡(luò)通道
Channel綁定,不會(huì)存在消費(fèi)者A接收到消費(fèi)者B的RPC響應(yīng)的情況; - 對(duì)于同一服務(wù)消費(fèi)者客戶端,在RPC請(qǐng)求
Request構(gòu)建時(shí)生成并攜帶全局唯一自增ID,RPC響應(yīng)Response會(huì)攜帶該ID返回。消費(fèi)者客戶端只需維護(hù) “唯一ID與RPC請(qǐng)求的關(guān)系Map<Long, DefaultFuture> FUTURES”即可定位RPC響應(yīng)對(duì)應(yīng)的RPC調(diào)用上下文;
五、Dubbo線程模型總結(jié)

Dubbo請(qǐng)求響應(yīng)線程模型圖
- 藍(lán)色代表“發(fā)送RPC請(qǐng)求”過(guò)程,由 業(yè)務(wù)請(qǐng)求線程 執(zhí)行,通過(guò)
NettyChannel將請(qǐng)求數(shù)據(jù)放入Netty的IO任務(wù)隊(duì)列后,構(gòu)建ResponseFuture并返回。此時(shí)RPC請(qǐng)求發(fā)送及響應(yīng)接收并未真正完成; - 紫色是基于
Netty的網(wǎng)絡(luò)消息收發(fā)過(guò)程,通過(guò)當(dāng)前網(wǎng)絡(luò)通道綁定的NioEventLoop線程輪詢完成; - 橙色代表“接收RPC響應(yīng)”過(guò)程,該過(guò)程在 Dubbo業(yè)務(wù)線程池 中執(zhí)行,處理RPC響應(yīng)消息并交由
ResponseFuture觸發(fā)接收響應(yīng)的邏輯; - 綠色代表“獲取RPC調(diào)用結(jié)果”過(guò)程,由 業(yè)務(wù)請(qǐng)求線程 執(zhí)行,阻塞直到從
ResponseFuture中獲取到RPC響應(yīng)結(jié)果; - 紅色代表“接收并處理RPC請(qǐng)求”過(guò)程,在 Dubbo業(yè)務(wù)線程池 中執(zhí)行。RPC請(qǐng)求消息由
HeaderExchangeHandler處理,通過(guò)DubboInvoker反射執(zhí)行 實(shí)際接口實(shí)現(xiàn)類 得到執(zhí)行結(jié)果,并封裝成Response交由網(wǎng)絡(luò)通道發(fā)送RPC響應(yīng)。
