24. Dubbo原理解析-編碼解碼之編碼解碼流程

這裏把ExchangeCodec和DubboCodec放一起來講解dubbo傳輸的底層協議組成以及它的編碼解碼過程。

 

傳輸協議

協議格式<header><bodydata>

協議頭 :header 是16個字節的定長數據

   =  2 //short類型的MAGIC = (short) 0xdabb

+ 1 //一個字節的消息標誌位,用來表示消息是request還是//response,twoway還是oneway,是心跳還是正常請求以及採用//的序列化反序列化協議

 + 1 //狀態位, 消息類型爲response時,設置請求響應狀態

 + 8 //設置消息的id long類型

 + 4 //設置消息體body長度 int類型

 

 

Body data:body是消息傳遞的真正內容,body的佔用的字節大小由協議頭後四位保存。Body的內容:

Request.getData()得到dubbo請求消息傳輸的body對象RpcInvocation,對於請求對象dubbo其實是知道需要傳輸哪些信息的,所以並沒有把整個RpcInvocation對象序列化傳輸,而是序列化傳輸必要字段信息,下面列舉出具體信息:

1. dubbo的版本信息

2. 服務接口名如:com.zhanqiu.DemoService

3. 服務的版本號

4. 調服務的方法名

5. 調服務的方法的參數描述符如:[int.class, boolean[].class,Object.class] => "I[ZLjava/lang/Object;"

6. 遍歷傳輸的參數值逐個序列化

7. 將整個附屬信息map對象attachments序列化

下圖是序列化Request的body的具體代碼:

 

Response.getResult()獲取Result對象。根據Result對象是否有異常對象序列裏化異常對象, 如果沒有獲取result.getValue()此對象爲真正返回的業務對象消息的的body數據,整體序列化result.getValue()返回的對象。

 

 

編碼整體流程:

1. 判斷消息類型是Request, Resonse如果不是調父類(可能是string telnet類型)

2. 獲取序列化方式, 可以同URL指定,沒有默認爲hessian方式

3. 構建存儲header的字節數組,大小是16

4. Header數組前兩位寫入dubbo協議魔數(short)0xdabb

5. Header數組第三位, 一個字節4位與或方式存儲,

1)     哪種序列化方式

2)     請求還是響應消息

3)     請求時twoway還是oneway

4)     是心跳,還是正常消息

5)     如果是response, 響應的狀態

6. 獲取buffer的寫入位置writerIndex, 加上消息頭長度16,重新設置buffer的寫入位置,這裏是消息body的寫入位置, 因爲後面是先寫body,要把header的位置空出來

7. 序列化消息body, (request, response參考前面的)寫入buffer

8. 計算消息體大小writerIndex – startIndex

9. 檢查消息體是否超過限制大小

10.      重置writeIndex就是第6點獲取的值

11.      給消息頭數組最後四位寫入消息消body長度值int類型

12.      向buffer寫入消息頭數據

13.      Buffer設置writerIndex=savedWriteIndex+ HEADER_LENGTH + len

 

 

解碼整體流程:

1. 從channle獲取可讀取的字節數readable

2. readable跟header_length取小構建字節數組header[]

readable < header_length說明不是一個全的dubbo協議消息(所以後面要判斷消息頭魔數),或者只是一個telnet消息

3. 如果判斷header[]的第一個和第二個字節不是dubbo協議魔數

a)  如果可讀取字節readable大於header_length, 重新構建header[], 讀取全部可讀取readable數據到header

b)  遍歷header中的字節判斷中間是否有dubbo協議的魔數0xdabb, 如果有說明魔數開始的是dubbo協議的消息。

重新設置buffer的讀取位置從魔數開始

截取header[]中從頭開始到魔術位置的數據

c)  調父類解碼,可能就是telnet字符串解碼

4. 如果是dubbo協議的消息 readable < header_length 說明讀取header[]數據不全, 返回NEED_MORE_INPUT說明需要讀取更多數據

5. 讀取header[]最後四位一個int的數據,bodydata的長度len

6. 計算總消息大小tt = len + body_length

7. 如果可讀取數據readable < tt, 數據不夠返回NEED_MORE_INPUT

8. 由buffer和len構建ChannelBufferInputStream, 後面序列化工具會使用

9. 下面是解碼消息體body data過程

9.1 header[2] 第三位獲取標誌位

9.2 從標誌位判斷採用哪種序列化方式,獲取具體的序列化工具

9.3 讀取header[4]開始的8位, 獲取消息的id

9.4 根據標誌位判讀消息爲響應類型

a)構建resonse對象 new Response(id)

b)根據標誌位如果是心跳,給response對象設置Event類型

c)從header[3]獲取消息響應狀態,給 response對象設置消息狀態

d) 如果是事件消息直接利用反序列化工具讀取對象

e) 如果不是構建消息接口DecodeableRpcResult result 序列化工具讀取請求結果並設置到result的value屬性上

f)如果響應狀態不是ok, 反序列化errMessage並設置給response

9.5 根據標誌位判斷消息爲請求類型

a) 根據id構建Request(id)

b) 根據狀態位設置請求模式 twoway還是oneway

c) 根據狀態位設置請求是否是事件類型

d) 如果是事件類型直接通過飯序列化工具讀取

e) 如果不是事件請求,構建DecodeableRpcInvocation

      通過反序列化工具

      讀取dubbo版本,設置到invocation的map中

      讀取path服務名,設置到invocation的map中

      讀取服務版本,設置到invocation的map中

      讀取調用服務的方法, 設置到invocation的methodName屬性上

      讀取方法描述符, 得到入參類型

      遍歷讀取入參參數值

      讀取map對象不爲空添加到invocation的map中
發佈了69 篇原創文章 · 獲贊 99 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章