1. 程式人生 > >Dubbo分析之Serialize層

Dubbo分析之Serialize層

Dubbo整體設計

關於Dubbo的整體設計可以檢視官方文件,下圖可以清晰的表達Dubbo的整體設計:

1.圖例說明

圖中左邊淡藍背景的為服務消費方使用的介面,右邊淡綠色背景的為服務提供方使用的介面,位於中軸線上的為雙方都用到的介面;

圖中從下至上分為十層,各層均為單向依賴,右邊的黑色箭頭代表層之間的依賴關係;

圖中綠色小塊的為擴充套件介面,藍色小塊為實現類,圖中只顯示用於關聯各層的實現類;

圖中藍色虛線為初始化過程,即啟動時組裝鏈,紅色實線為方法呼叫過程,即執行時調時鏈,紫色三角箭頭為繼承,可以把子類看作父類的同一個節點,線上的文字為呼叫的方法;

2.各層說明

config 配置層:對外配置介面,以 ServiceConfig, ReferenceConfig 為中心,可以直接初始化配置類,也可以通過 spring 解析配置生成配置類;

proxy 服務代理層:服務介面透明代理,生成服務的客戶端 Stub 和伺服器端 Skeleton, 以 ServiceProxy 為中心,擴充套件介面為 ProxyFactory;

registry 註冊中心層:封裝服務地址的註冊與發現,以服務 URL 為中心,擴充套件介面為 RegistryFactory, Registry, RegistryService;

cluster 路由層:封裝多個提供者的路由及負載均衡,並橋接註冊中心,以 Invoker 為中心,擴充套件介面為 Cluster, Directory, Router, LoadBalance;

monitor 監控層:RPC 呼叫次數和呼叫時間監控,以 Statistics 為中心,擴充套件介面為 MonitorFactory, Monitor, MonitorService;

protocol 遠端呼叫層:封裝 RPC 呼叫,以 Invocation, Result 為中心,擴充套件介面為 Protocol, Invoker, Exporter;

exchange 資訊交換層:封裝請求響應模式,同步轉非同步,以 Request, Response 為中心,擴充套件介面為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer;

transport 網路傳輸層:抽象 mina 和 netty 為統一介面,以 Message 為中心,擴充套件介面為 Channel, Transporter, Client, Server, Codec;

serialize 資料序列化層:可複用的一些工具,擴充套件介面為 Serialization, ObjectInput, ObjectOutput, ThreadPool;

本文將從最底層的serialize層開始來對dubbo進行原始碼分析;

通訊框架

dubbo的底層通訊使用的是第三方框架,包括:netty,netty4,mina和grizzly;預設使用的是netty,分別提供了server端(服務提供方)和client端(服務消費方);下面已使用的netty為例來看那一下NettyServer的部分程式碼:

在啟動服務提供方時就會呼叫此doOpen方法,用來啟動服務埠,供消費方連線;以上程式碼就是常規的啟動nettyServer端程式碼,因為本文重點介紹dubbo的序列化,所以這裡主要看decoder和encoder,這兩個類分別定義在NettyCodecAdapter中:

1.編碼器

在NettyCodecAdapter定義了內部類InternalEncoder:

此類其實是對codec的包裝,本身並沒有做編碼處理,下面重點看一下codec類,此類是一個介面類,有多種實現類,Codec2原始碼如下:


實現包括:TransportCodec,TelnetCodec,ExchangeCodec,DubboCountCodec以及ThriftCodec,當然也可以自行擴充套件;不可能啟動時把每種型別都載入,dubbo是通過在配置檔案中配置好所有的型別,然後在執行中需要什麼類載入什麼類,

配置檔案的具體路徑:META-INF/dubbo/internal/com.alibaba.dubbo.remoting.Codec2,內容如下:

獲取具體Codec2的程式碼如下:

通過在url中獲取是否有關鍵字codec,如果有的話就獲取當前的值,dubbo預設的codec為dubbo;如果沒有值預設為telnet;這裡有預設值為dubbo,所以實現類DubboCountCodec會被ExtensionLoader進行載入並進行快取,下面具體看一下DubboCountCodec的編解碼;

DubboCountCodec內部呼叫的是DubboCodec的encode方法,看一下如何對Request物件進行編碼的,具體程式碼塊如下:

前兩個位元組存放了魔數:0xdabb;第三個位元組包含了四個資訊分別是:是否是請求訊息(還是響應訊息),序列化型別,是否雙向通訊,是否是心跳訊息;

在請求訊息中直接跳過了第四個位元組,直接在5-12位置存放了requestId,是一個long型別,第四個位元組在如果是編碼響應訊息中會存放響應的狀態;

程式碼往下看,buffer跳過了HEADER_LENGTH長度的位元組,這裡表示的是header部分的長度為16個位元組,然後通過指定的序列化方式把data物件序列化到buffer中,序列化之後可以獲取到data物件總共的位元組數,用一個int型別來儲存位元組數,此int型別存放在header的最後四個位元組中;

最後把buffer的writerIndex設定到寫完header和data的地方,防止資料被覆蓋;

2.解碼器

在NettyCodecAdapter定義了內部類InternalEncoder,同樣是呼叫DubboCodec的decode方法,部分程式碼如下:

首先讀取Math.min(readable, HEADER_LENGTH),如果readable小於HEADER_LENGTH,表示接收方連頭部的16個位元組還沒接受完,需要等待接收;正常header接收完之後需要進行檢查,主要包括:魔數的檢查,header訊息長度檢查,訊息體長度檢查(檢查訊息體是否已經接收完成);檢查完之後需要對訊息體進行反序列化,具體在decodeBody方法中:

首先通過解析header部分的第三個位元組,識別出是請求訊息還是響應訊息,還有使用哪種型別的序列化方式,然後分別進行序列化;

序列化和反序列化

通過以上對編碼器解碼器的瞭解,在編碼器中需要序列化Request/Response,在解碼器中需要序列化Request/Response,下面具體看看序列化和反序列化;

1.序列化

在編碼器中需要獲取具體的Serialization,具體程式碼如下:

同獲取codec的方式,dubbo也提供了多種序列化方式,同時可以自定義擴充套件;通過在url中獲取serialization關鍵字,如果獲取不到預設為hession2;同樣多種序列化類也配置在一個檔案中,

路徑:META-INF/dubbo/internal/com.alibaba.dubbo.common.serialize.Serialization,具體內容如下:

dubbo預設提供了fastjson,fst,hessian2,java,compactedjava,nativejava和kryo多種序列化方式;

每種序列化方式都需要實現如下三個介面類:Serialization,ObjectInput以及ObjectOutput;

Serialization介面類:

其中的ContentTypeId就是在header中存放的序列化型別,反序列化的時候需要通過此id獲取具體的Serialization,所以此ContentTypeId不能出現重複的,否則會被覆蓋;

ObjectInput介面類:


ObjectOutput介面類:

分別提供了讀取物件和寫物件的介面方法,DataOutput和DataInput分別提供了對基本資料型別的讀和寫;序列化只需要呼叫writeObject方法將Data寫入資料流即可;具體可以看一下編碼器中呼叫的encodeRequestData方法:


預設使用的DubboCountCodec方式並沒有直接將data寫入流中,而是將RpcInvocation中的資料取出分別寫入流;

2.反序列化

反序列化通過讀取header中的序列化型別,然後通過如下方法獲取具體的Serialization,具體在類CodecSupport中:


ID_SERIALIZATION_MAP存放著ContentTypeId和具體Serialization的對應關係,然後通過id獲取具體的Serialization,然後根據寫入的順序讀取資料;

擴充套件序列化型別

dubbo本身對很多模組提供了很好的擴充套件功能,包括序列化功能,以下來分析一下如何使用protobuf來實現序列化方式;

1.整體程式碼結構

首先看一下整體的程式碼結構,如下圖所示:

分別實現三個介面類:Serialization,ObjectInput以及ObjectOutput;然後在指定目錄下提供一個文字檔案;

2.引入擴充套件包

3.實現介面ObjectInput和ObjectOutput

4.實現Serialization介面


這裡引入了一個新的ContentTypeId,需要保證和dubbo裡面已存在的不要衝突

5.指定目錄提供註冊

在META-INF/dubbo/internal/目錄下提供檔案com.alibaba.dubbo.common.serialize.Serialization,內容如下:


6.在提供方配置新的序列化方式

這樣就會使用新擴充套件的protobuf序列化方式來序列化物件;