自定义_如何自定义协议
前言
何為自定義協(xié)議,其實是相對標(biāo)準(zhǔn)協(xié)議來說的,這里主要針對的是應(yīng)用層協(xié)議;常見的標(biāo)準(zhǔn)的應(yīng)用層協(xié)議如http、ftp、smtp等,如果我們在網(wǎng)絡(luò)通信的過程中不去使用這些標(biāo)準(zhǔn)協(xié)議,那就需要自定義協(xié)議,比如我們常用的RPC框架(dubbo,thrift),分布式緩存(redis,memcached)等都是自定義協(xié)議;本文就來講講如何去自定義私有協(xié)議,在此之前我們先考慮一下為什么要自定義協(xié)議。
為什么要自定義協(xié)議
直接使用標(biāo)準(zhǔn)的協(xié)議好處是顯而易見的,我個人理解的幾點優(yōu)點:
既然是標(biāo)準(zhǔn)協(xié)議說明已經(jīng)成為了標(biāo)準(zhǔn),這樣很多系統(tǒng)就可以直接對接,無縫集成;
協(xié)議最重要的一點就是編碼解碼,標(biāo)準(zhǔn)協(xié)議往往有現(xiàn)成的編碼解碼包,直接拿來使用,減少開發(fā)時間;
有很多圍繞標(biāo)準(zhǔn)協(xié)議的第三方測試工具,可以很方便的進行測試;
既然有這么多優(yōu)點那我們?yōu)槭裁催€要去自定義協(xié)議,大致出于以下幾點考慮:
既然是標(biāo)準(zhǔn)協(xié)議,往往兼顧的東西比較多,導(dǎo)致協(xié)議數(shù)據(jù)相對來說比較大,這樣可能在一些追求性能,流量的系統(tǒng)中不能容忍;
標(biāo)準(zhǔn)協(xié)議有很多,沒有哪一種協(xié)議可以適用任何場景中,所以如果在某個場景中還沒有既定的標(biāo)準(zhǔn)協(xié)議,這時候會有各種私有協(xié)議;
自定義協(xié)議只要雙方約定好數(shù)據(jù)結(jié)構(gòu)就行,不具有通用性,理論上來說會更加安全一點,當(dāng)然現(xiàn)在很多標(biāo)準(zhǔn)協(xié)議都有安全版本,比如https,sftp等等;
以上只是個人的一點理解,歡迎大家補充;關(guān)于如何去自定義協(xié)議,其實可以去多參考一些主流的標(biāo)準(zhǔn)協(xié)議或者私有協(xié)議,其實有很多共同點可以去借鑒;下面先簡單看看那些主流的協(xié)議;
主流協(xié)議
下面分別看看一些主流的標(biāo)準(zhǔn)協(xié)議或者私有協(xié)議都是如何去定義自己的數(shù)據(jù)結(jié)構(gòu)的,對我們有非常好的借鑒意義;
http協(xié)議
http協(xié)議大家最熟悉不過了,全稱叫超文本傳輸協(xié)議,整個請求報文可以分為三個部分分別是:請求行,請求報頭,請求正文;
請求行
請求報頭
Content-Length: 38
Content-Encoding: gzip
...
請求包頭有很多,每一個代表了各自的含義,這邊就不一一列出,我們這里更加關(guān)注整個報文的結(jié)構(gòu);
請求正文
這個只有在POST請求的時候才有正文,里面存放業(yè)務(wù)數(shù)據(jù),比如常見的json文本串;具體正文的長度可以根據(jù)消息頭中的Content-Length來決定;
dubbo協(xié)議
dubbo協(xié)議格式可以直接參考官網(wǎng)提供的如下圖片:??看上圖其實整個協(xié)議數(shù)據(jù)包也大致分為兩個部分:固定部分和可變部分,或者叫消息頭和消息體;固定部分一共是4+8+4=16個字節(jié),具體如下所示:
header{Magic High = 8bit; //魔數(shù)高位
Magic Low = 8bit; //魔數(shù)低位
Req/Res = 1bit; //標(biāo)識是請求或響應(yīng)
2 Way = 1bit; //標(biāo)記是否期望從服務(wù)器返回值
Event = 1bit; //標(biāo)識是否是事件消息
Serialization ID = 5bit; //標(biāo)識序列化類型
Status = 8bit; //標(biāo)識響應(yīng)的狀態(tài)
Request ID = 64bit; //標(biāo)識唯一請求
Data Length = 32bit; //序列化后的內(nèi)容長度
}
可變部分根據(jù)固定部分中的Data Length來確定長度;
redis協(xié)議
Redis的客戶端與服務(wù)端采用叫做 **RESP(Redis Serialization Protocol)**的網(wǎng)絡(luò)通信協(xié)議交換數(shù)據(jù),相對來說還是比較簡單的,以下是這個協(xié)議的一般形式:
*< 參數(shù)數(shù)量 > CR LF$< 參數(shù) 1 的字節(jié)數(shù)量 > CR LF
< 參數(shù) 1 的數(shù)據(jù) > CR LF
...
$< 參數(shù) N 的字節(jié)數(shù)量 > CR LF
< 參數(shù) N 的數(shù)據(jù) > CR LF
以上大致介紹了三種比較有代表性的協(xié)議,雖然說每種協(xié)議都有各自的使用場景,但是如果我們自己去定義協(xié)議,還是有一些相通的東西;
如何自定義協(xié)議
下面我們重點看看去自定義協(xié)議有哪些需要我們關(guān)注的點,以下是本人根據(jù)自己的理解整理了如下關(guān)注點:
完整的數(shù)據(jù)包
協(xié)議號
消息頭標(biāo)識
業(yè)務(wù)數(shù)據(jù)
預(yù)留字段
下面分別逐一詳細(xì)介紹:
完整的數(shù)據(jù)包
我們平時經(jīng)常講數(shù)據(jù)包,但是TCP其實只有流的概念,并沒有數(shù)據(jù)包的概念;那很重要的一點就是我們的程序怎么知道現(xiàn)在的業(yè)務(wù)數(shù)據(jù)已經(jīng)接受全部接收完了,可以作為一個完整的數(shù)據(jù)包去處理了,如果不去做處理的話就會出現(xiàn)我們常說的半包和粘包問題;主流的的處理方式大致有這么兩種:
在消息頭部加上數(shù)據(jù)包長度描述,比如在http協(xié)議和dubbo協(xié)議中出現(xiàn)的dataLength字段;
用特殊的字符串作為數(shù)據(jù)包的結(jié)尾,這樣我們在接受數(shù)據(jù)的時候接受到預(yù)定的特殊字符串就表示數(shù)據(jù)包完整了;
協(xié)議號
可能不同的協(xié)議有不同的叫法,我這里把它叫做協(xié)議號,個人理解就是根據(jù)這個協(xié)議號,服務(wù)器端知道去執(zhí)行什么邏輯;比如http協(xié)議請求行中的/test.html,dubbo協(xié)議中的服務(wù)名+版本號,redis中的具體要執(zhí)行什么key;
消息頭標(biāo)識
這個是否需要還是要看各自的場景,比如redis協(xié)議足夠簡單,無需任何標(biāo)識,所有的東西都是雙端約定好的;但是其他很多協(xié)議還是有一些需要的,除了上面說到的可以在消息頭中指定dataLength,其實還有很多其他的東西可以指定比如:
業(yè)務(wù)數(shù)據(jù)格式:文本格式,json格式,html格式等等;
壓縮格式:可能為了追求流量包大小對數(shù)據(jù)包進行壓縮,gzip、deflater、snappy等;
加密算法:可能需要對我的業(yè)務(wù)數(shù)據(jù)進行加密處理,保證業(yè)務(wù)數(shù)據(jù)的安全性AES、DES等;
業(yè)務(wù)數(shù)據(jù)
業(yè)務(wù)數(shù)據(jù)往往在整個數(shù)據(jù)包中是最大的,同時也是大小可變的部分;我們上面所做的這些其實都是在為業(yè)務(wù)數(shù)據(jù)服務(wù),業(yè)務(wù)數(shù)據(jù)需要在網(wǎng)絡(luò)傳輸,最重要的一點就是序列化,一般就以下兩種方式:
文本方式:序列化文本文檔text,或者json串,xml格式等;
二進制方式:常見的比如protobuf,thrift,kyro等;
預(yù)留字段
是否需要預(yù)留字段這個得看情況,比如http協(xié)議整個消息頭是可變的,每一行一個標(biāo)識,知道讀取到空行,表示消息頭結(jié)束下面就是正文了,可以理解為http使用了兩種方式來保證完整包,消息頭使用特殊字符結(jié)尾,正文使用在消息頭中指定dataLength;這種方式其實它的整個擴展性是非常好的;另外一種像dubbo這樣,其實它的頭部相當(dāng)于已經(jīng)固定好了16個字節(jié),這種情況下是否可以預(yù)留幾個字節(jié)防止后面的變更;
總結(jié)
自定義協(xié)議其實在我們真正的工作中還是很少能接觸到的,更多的其實還是去實現(xiàn)業(yè)務(wù),但是我們系統(tǒng)無時無刻不在和各種應(yīng)用層協(xié)議打交道,如果我們了解了各種協(xié)議,在系統(tǒng)出現(xiàn)問題時可以做抓包分析;另外像我們常用的數(shù)據(jù)庫中間件、緩存中間件等,都需要對協(xié)議都充分的了解,然后去實現(xiàn)代理。
感謝關(guān)注
可以關(guān)注微信公眾號「回滾吧代碼」,第一時間閱讀,文章持續(xù)更新;專注Java源碼、架構(gòu)、算法和面試。
- END -往期推薦
RPC框架設(shè)計概要
《大廠面試》之線上故障排查
《生產(chǎn)事故》之反射引發(fā)的宕機問題
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的自定义_如何自定义协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux常用指令自己备用
- 下一篇: (二十四)【2021 ACL】OntoE