聊聊dubbo協議

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 本文已收錄 https://github.com/lkxiaolou/lkxiaolou 歡迎star。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"協議","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"協議","attrs":{}}],"attrs":{}},{"type":"text","text":"通俗易懂地解釋就是通信雙方需要遵循的約定。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們瞭解的常見的網絡傳輸協議有tcp、udp、http等。再到我們常用的基礎組件,一般來說client端與server端也有相應的協議,如redis、mysql、zookeeper等都是各自約定的私有協議,同樣今天標題中的dubbo協議也是一種私有協議,他們都是應用層協議,基於tcp或udp設計。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常應用層協議都是基於tcp和udp,可靠傳輸通常使用tcp,如大多數的基礎組件,如redis、mysql。只有能容忍丟失且需要很高的性能時使用udp協議,比如metric上報等場景。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏介紹幾種基於tcp的應用協議。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"redis協議","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis協議足夠簡單,所以先介紹一下。redis協議基於tcp設計,客戶端和服務器發送的命令一律使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"\\r\\n","attrs":{}}],"attrs":{}},{"type":"text","text":"(CRLF)結尾。他的格式如下","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"* CRLF\n$ CRLF\n CRLF\n...\n$ CRLF\n CRLF\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子,client向server端發送命令 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"set mykey myvalue","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"*3 CRLF\n$3 CRLF\nSET CRLF\n$5 CRLF\nmykey CRLF\n$7 CRLF\nmyvalue CRLF\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*3\\r\\n$3\\r\\nSET\\r\\n$5\\r\\nmykey\\r\\n$7\\r\\nmyvalue\\r\\n","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於redis協議更詳細信息可以看這個鏈接:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"http://redisdoc.com/topic/protocol.html","attrs":{}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"http協議","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"http協議是我們最常見的協議,它的請求報文格式是由三部分組成:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求行:包括method、url、version,由空格分隔,\\r\\n結尾","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求頭:多行,每行是key:value的格式,以\\r\\n結尾","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求體:請求頭與請求體直接由一個空白行分隔,請求體的長度在請求頭中由","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"content-length","attrs":{}}],"attrs":{}},{"type":"text","text":"給出","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis和http協議的處理方式截然不同。他們都是基於tcp,而tcp協議傳輸的數據是流式的,通俗地說就是它就像水流,不斷地發送字節,tcp保證不重複,不丟包。而接收端要拿到想要的數據必須得從流式的數據中“判斷出數據包的邊界”,這就是tcp的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"粘包","attrs":{}}],"attrs":{}},{"type":"text","text":"問題,解決它通常有三種方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"發送固定長度的消息","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"使用特殊標記區分消息間隔","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"將消息的尺寸和消息一起發送","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis協議使用了第2種,http和接下來要介紹的dubbo協議使用了第3種,固定長度的消息比較理想,在實際中很少遇到。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"dubbo協議","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於dubbo支持的協議很多,本文提到的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dubbo協議","attrs":{}}],"attrs":{}},{"type":"text","text":"特指dubbo框架的默認協議,也就是dubbo的私有協議。它的格式如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"0-15: 魔數,判斷是否是dubbo協議","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"16: 判斷是請求還是返回","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"17: 判斷是否期望返回","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"18: 判斷是否爲事件消息,如心跳事件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"19-23: 序列化標誌","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"24-31: 標誌響應狀態(類似http status)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"32-63: 請求id","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"64-95: 內容長度(字節)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"96-?: 序列化後的內容(換行符分隔)","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a7/a7968c4cc1b12588e2a92d7fa45a96f6.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"常用的attachments在dubbo協議的哪裏?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dubbo的attachments,我們通常將他類比爲http協議的header,可以攜帶一些","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"隱式","attrs":{}}],"attrs":{}},{"type":"text","text":"的參數信息(不用編碼到請求對象中),如壓測標誌等。從他的類型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private Map attachments;\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基本可以推斷出attachments存在於dubbo協議的96字節之後的內容中,因爲前面頭的根本放不下這個map。從dubbo的實現中可以看出,dubbo的一個請求被封裝爲一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DecodeableRpcInvocation","attrs":{}}],"attrs":{}},{"type":"text","text":"對象,裏面包含了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"methodName","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"parameterTypes","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arguments","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"attachments","attrs":{}}],"attrs":{}},{"type":"text","text":"等,將該對象序列化後填入dubbo協議的96字節後的內容中發送出去。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用時,consumer端:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"RpcContext.getContext().setAttachment(\"hello\", \"from_consumer\");\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"provider端:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"RpcContext.getContext().getAttachment(\"hello\");\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏能看出dubbo協議相比較http協議來說設計的還是有所欠缺的,想要拿到一些隱式參數,或者想要知道請求發往哪裏,必須得把請求體解析出來纔可以,這也是dubbo協議往mesh方向發展的一個絆腳石。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"dubbo協議支持在返回值中帶回attachments嗎?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"consumer端向provider端發送請求可以在頭部攜帶隱式參數,那麼返回時也可以從provider端帶回到consumer端嗎?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如provider回傳給consumer它自身的處理耗時,consumer計算出請求的響應時間,兩者相減即可得到網絡耗時,此時provider端最好是將耗時放在attachments中隱式地傳回。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dubbo的協議是請求和回覆都是相同格式,理論上consumer可以帶隱式參數到provider端,則provider端肯定也可以回傳。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從dubbo的返回對象","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DecodeableRpcResult","attrs":{}}],"attrs":{}},{"type":"text","text":"中可以看到是存在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"attachments","attrs":{}}],"attrs":{}},{"type":"text","text":"的,但從實際的測試來看,2.7.x版本是不支持的,但2.6.x(>=2.6.3)版本是支持的。provider端設置:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"RpcContext.getServerContext().setAttachment(\"hello\", \"from_provider\");\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"consumer端獲取:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"RpcContext.getServerContext().getAttachment(\"hello\")\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"github上相關的issue鏈接如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"https://github.com/apache/dubbo/pull/1843","attrs":{}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"協議和序列化有什麼區別?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可能會經常聽到這樣的說法,dubbo除了dubbo協議外還支持rest、thrift、grpc等協議,也支持hessian、json序列化。協議與序列化是什麼關係?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過剛剛介紹的dubbo協議格式或許就能明白,dubbo協議是如上的格式包含了頭和內容,其中96字節之後的內容是序列化後的內容,默認使用hessian2序列化,也可以修改爲fastjson、gson、jdk等。只需要配置即可修改協議","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"xml"},"content":[{"type":"text","text":"\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果非要做個類比的話,就是你不僅可以通過http協議傳輸json格式的數據,也可以傳輸xml格式的數據。http就是協議,json和xml就是序列化。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"最後","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dubbo協議的設計雖然有所欠缺,但依然不能阻止它成爲dubbo使用最廣泛的協議。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於作者:專注後端的中間件開發,公衆號\"捉蟲大師\"作者,關注我,給你最純粹的技術乾貨","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b9/b933af2d4f703f3db8f6f02f83b1f13f.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章