聊聊dubbo協議


搜索關注微信公眾號"捉蟲大師",后端技術分享,架構設計、性能優化、源碼閱讀、問題排查、踩坑實踐。

協議

協議通俗易懂地解釋就是通信雙方需要遵循的約定。

我們了解的常見的網絡傳輸協議有tcp、udp、http等。再到我們常用的基礎組件,一般來說client端與server端也有相應的協議,如redis、mysql、zookeeper等都是各自約定的私有協議,同樣今天標題中的dubbo協議也是一種私有協議,他們都是應用層協議,基於tcp或udp設計。

通常應用層協議都是基於tcp和udp,可靠傳輸通常使用tcp,如大多數的基礎組件,如redis、mysql。只有能容忍丟失且需要很高的性能時使用udp協議,比如metric上報等場景。

這里介紹幾種基於tcp的應用協議。

redis協議

redis協議足夠簡單,所以先介紹一下。redis協議基於tcp設計,客戶端和服務器發送的命令一律使用\r\n(CRLF)結尾。他的格式如下

*<參數數量> CRLF
$<參數1 字節數量> CRLF
<參數1的數據> CRLF
...
$<參數n 字節數量> CRLF
<參數n的數據> CRLF

舉個例子,client向server端發送命令 set mykey myvalue

*3 CRLF
$3 CRLF
SET CRLF
$5 CRLF
mykey CRLF
$7 CRLF
myvalue CRLF

也就是 *3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n

關於redis協議更詳細信息可以看這個鏈接:

http://redisdoc.com/topic/protocol.html

http協議

http協議是我們最常見的協議,它的請求報文格式是由三部分組成:

  • 請求行:包括method、url、version,由空格分隔,\r\n結尾
  • 請求頭:多行,每行是key:value的格式,以\r\n結尾
  • 請求體:請求頭與請求體直接由一個空白行分隔,請求體的長度在請求頭中由content-length給出

redis和http協議的處理方式截然不同。他們都是基於tcp,而tcp協議傳輸的數據是流式的,通俗地說就是它就像水流,不斷地發送字節,tcp保證不重復,不丟包。而接收端要拿到想要的數據必須得從流式的數據中“判斷出數據包的邊界”,這就是tcp的粘包問題,解決它通常有三種方法:

  1. 發送固定長度的消息
  2. 使用特殊標記區分消息間隔
  3. 將消息的尺寸和消息一起發送

redis協議使用了第2種,http和接下來要介紹的dubbo協議使用了第3種,固定長度的消息比較理想,在實際中很少遇到。

dubbo協議

由於dubbo支持的協議很多,本文提到的dubbo協議特指dubbo框架的默認協議,也就是dubbo的私有協議。它的格式如下:

  • 0-15: 魔數,判斷是否是dubbo協議
  • 16: 判斷是請求還是返回
  • 17: 判斷是否期望返回
  • 18: 判斷是否為事件消息,如心跳事件
  • 19-23: 序列化標志
  • 24-31: 標志響應狀態(類似http status)
  • 32-63: 請求id
  • 64-95: 內容長度(字節)
  • 96-?: 序列化后的內容(換行符分隔)

常用的attachments在dubbo協議的哪里?

dubbo的attachments,我們通常將他類比為http協議的header,可以攜帶一些隱式的參數信息(不用編碼到請求對象中),如壓測標志等。從他的類型

private Map<String, String> attachments;

基本可以推斷出attachments存在於dubbo協議的96字節之后的內容中,因為前面頭的根本放不下這個map。
從dubbo的實現中可以看出,dubbo的一個請求被封裝為一個DecodeableRpcInvocation對象,里面包含了methodNameparameterTypesargumentsattachments等,將該對象序列化后填入dubbo協議的96字節后的內容中發送出去。

使用時,consumer端:

RpcContext.getContext().setAttachment("hello", "from_consumer");

provider端:

RpcContext.getContext().getAttachment("hello");

這里能看出dubbo協議相比較http協議來說設計的還是有所欠缺的,想要拿到一些隱式參數,或者想要知道請求發往哪里,必須得把請求體解析出來才可以,這也是dubbo協議往mesh方向發展的一個絆腳石。

dubbo協議支持在返回值中帶回attachments嗎?

consumer端向provider端發送請求可以在頭部攜帶隱式參數,那么返回時也可以從provider端帶回到consumer端嗎?

比如provider回傳給consumer它自身的處理耗時,consumer計算出請求的響應時間,兩者相減即可得到網絡耗時,此時provider端最好是將耗時放在attachments中隱式地傳回。

dubbo的協議是請求和回復都是相同格式,理論上consumer可以帶隱式參數到provider端,則provider端肯定也可以回傳。

從dubbo的返回對象DecodeableRpcResult中可以看到是存在attachments的,但從實際的測試來看,2.7.x版本是不支持的,但2.6.x(>=2.6.3)版本是支持的。
provider端設置:

RpcContext.getServerContext().setAttachment("hello", "from_provider");

consumer端獲取:

RpcContext.getServerContext().getAttachment("hello")

github上相關的issue鏈接如下:

https://github.com/apache/dubbo/pull/1843

協議和序列化有什么區別?

我們可能會經常聽到這樣的說法,dubbo除了dubbo協議外還支持rest、thrift、grpc等協議,也支持hessian、json序列化。協議與序列化是什么關系?

通過剛剛介紹的dubbo協議格式或許就能明白,dubbo協議是如上的格式包含了頭和內容,其中96字節之后的內容是序列化后的內容,默認使用hessian2序列化,也可以修改為fastjson、gson、jdk等。只需要配置即可修改協議

<dubbo:protocol name="dubbo" serialization="fastjson"/>

如果非要做個類比的話,就是你不僅可以通過http協議傳輸json格式的數據,也可以傳輸xml格式的數據。http就是協議,json和xml就是序列化。

最后

dubbo協議的設計雖然有所欠缺,但依然不能阻止它成為dubbo使用最廣泛的協議。


搜索關注微信公眾號"捉蟲大師",后端技術分享,架構設計、性能優化、源碼閱讀、問題排查、踩坑實踐。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM