Dubbo協定詳解
Dubbo協定設計參考了現有TCP/IP協定。一次RPC調用包括協定頭和協定體兩個部分。
16位元組
長的封包頭部主要攜帶了魔法數
(0xdabb)
,以及目前請求封包是否是Request、REsponse、心跳和事件的資訊,請求時也會攜帶目前封包體内序列化協定編号。除此之外還攜帶了請求狀态,以及請求唯一辨別和封包體長度。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSNGdkY3lTMYZmUHJmdOJDTwYVbiVHNHpleO1GTulzRilWO5xkNNh0YwIFSh9Fd4VGdsATMfd3bkFGazxyaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SN3ETM1AjMwMjNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Dubbo協定字段解析:
偏移比特位 | 字段描述 | 作用 |
---|---|---|
0~7 | 魔數高位 | 存儲的是魔法數高位(0xda00) |
8~15 | 魔數低位 | 存儲的是魔法數低位(0xbb) |
16 | 資料包類型 | 是否為雙向RPC調用(比如方法調用有傳回值),0為Response,1為Request |
17 | 調用方式 | 僅在第16位被這設為1的情況下有效 0為單向調用,1位雙向調用 比如在優雅停機時服務端發送readoly不需要雙向調用,這裡标志位就不會設定 |
18 | 事件辨別 | 0為目前資料包是請求或響應包 1為目前資料包是心跳包,比如架構為了保活TCP連接配接,每次用戶端和服務端互相發送心跳包時這個标志位被設定 設定了心跳封包不會透傳到業務方法調用,僅用于架構内部保活機制 |
19~23 | 序列化器編号 | 2為HessianSerializatiion 3為JavaSerialization 4為CompactedJavaSerialization 6為FastJsonSerialization 7為NativJavaSerialization 8為KryoSerialization 9為FstSerialization |
24~31 | 狀态 | 20為OK 30為CLIENT_TIMEOUT 31為SERVER_TIMEOUT 40為BAD_REQUEST 50為BAD_RESPONSE … |
32~95 | 請求編号 | 這8個位元組存儲RPC請求的唯一id,用來将請求和響應做關聯 |
96~127 | 消息體長度 | 占用的4個位元組存儲消息體長度。在一次RPC請求過程中,消息體中一次會存儲7部分内容 |
在消息體中,用戶端嚴格按照序列化順序寫入消息,服務端也會遵循相同的順序讀取消息,用戶端發起請求的消息體一次儲存下列内容:Dubbo版本号、服務接口名、服務接口版本、方法名、參數類型、方法參數值和請求額外參數(attachment)。
完整狀态響應碼和作用:
狀态值 | 狀态符号 | 作用 |
---|---|---|
20 | OK | 正确傳回 |
30 | CLIENT_TIMEOUT | 用戶端逾時 |
31 | SERVER_TIMEOUT | 服務端逾時 |
40 | BAD_REQUEST | 請求封包格式錯誤 |
50 | BAD_RESPONSE | 響應封包格式錯誤 |
60 | SERVICE_NOT_FOUND | 未找到比對的服務 |
70 | SERVICE_ERROR | 服務調用錯誤 |
80 | SERVER_ERROR | 服務端内部錯誤 |
90 | CLIENT_ERROR | 用戶端錯誤 |
100 | SERVER_THREADPOOL_EXHAUSTED_ERROR | 服務端線程池滿拒絕執行 |
Dubbo響應标記:
狀态值 | 狀态符号 | 作用 |
---|---|---|
5 | RESPONSE_NULL_VALUE_WITH_ATTACHEMENTS | 響應空值包含隐藏參數 |
4 | RESPONSE_VALUE_WITH_ATTACHEMENTS | 響應結果包含隐藏參數 |
3 | RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS | 異常傳回包含隐藏參數 |
2 | RESPONSE_NULL_VALUE | 響應空值 |
1 | RESPONSE_VALUE | 響應結果 |
RESPONSE_WITH_EXCEPTION | 異常傳回 |
在傳回消息體中,會先把傳回值狀态标記寫入輸入流,根據标記狀态判斷RPC是否正常,比如一次正常RPC調用成功,則先往消息體中寫一個标記1,緊接着再寫方法傳回值。
在網絡通信中(基于TCP)需要解決網絡粘包/解包的問題,一些常用解決辦法比如用回車、換行、固定長度和特殊分隔符等進行處理,通過對前面協定的了解,很容易發現Dubbo其實就是用特殊符号
0xdabb魔數
來分隔處理粘包問題的。
用戶端會使用多線程并發調用服務,Dubbo是如何做到正确響應調用線程的呢?
當用戶端多個線程并發請求時,架構内部會調用Defaultfutre對象的get方法進行等待。在請求發起時,架構内部會建立Request對象,這個時候會被配置設定一個唯一的id,DefaultFuture可以從Request對象中擷取id,并将關聯關系存儲到靜态HashMap中,就是上圖中的Future集合。當用戶端收到響應時,會根據Response對象中的id,從Futures集合中查找對應的DefaultFuture對象,最終會喚醒對應的線程并通知結果。用戶端也會啟動一個定時掃描線程去探測逾時沒有傳回的請求。