RPC框架實現 通訊協議篇

2021-09-08 22:15:31 字數 3814 閱讀 6082

rpc(remote procedure call,遠端過程呼叫)框架是分布式服務的基石,實現rpc框架需要考慮方方面面。其對業務隱藏了底層通訊過程(tcp/udp、打包/解包、序列化/反序列化),使上層專注於功能實現;框架層面,提供各類可選架構(多程序/多執行緒/協程);應對裝置故障(高負載/宕機)、網路故障(擁塞/網路分化),提供相應容災措施。

rpc節點間為了協同工作、實現資訊交換,需要協商一定的規則和約定,例如位元組序、壓縮或加密演算法、各欄位型別。通訊協議的應用隨處可見,例如我們對可選資訊或字段經常使用tlv進行編碼,http、ftp等協議基於可讀文字的 "field: value" 格式,各種系統也經常使用json、xml格式完成相互間通訊。

不同的通訊協議適用於不同的應用場景,比如內部系統的互動我們選擇json,一來可讀性較好,二來各種語言都提供了解析json的庫、方便編碼。google protocol buffers是生成環境中常用的通訊協議,除了可以設定client/server間通訊格式,protocol buffers還對資料進行壓縮,節省傳輸流量、加快傳輸速度。下面我們來了解google protocol buffers。

protocol buffers

我們看如何使用protocol buffers(以下簡稱pb),首先在.proto檔案中定義資料格式,下面以person.proto為例:

message person 

message phonenumber

repeated phonenumber phone = 4

;}

完成資料定義後,接下來可以使用protoc工具解析person.proto檔案,生成person類:

protoc --cpp_out=/home/bangerlee/pb ./person.proto
執行以上命令後,可以看到 /home/bangerlee/pb 目錄下生成了兩個檔案:

person.pb.cc  person.pb.h
其中定義了操作(get/set)person類各個欄位的函式。

有了介面,我們就可以在**中這樣使用person類,寫入操作如下:

person person;

person.set_name(

"bangerlee");

person.set_id(

1234

);person.set_email(

"[email protected]");

fstream output(

"myfile

", ios::out |ios::binary);

person.serializetoostream(&output);

讀取操作如下:

fstream input("

myfile

", ios::in |ios::binary);

person person;

person.parsefromistream(&input);

cout

<< "

name:

"<< person.name() <

cout

<< "

e-mail:

"<< person.email() << endl;

以上我們初步了解了如何使用pb,pb運用了一些編碼規則,使得需要傳輸的資料(二進位制格式)更小,下面我們就來了解pb如何對不同資料型別的編碼規則。

編碼(encoding)

對整形int、字串型別string等,pb有不同的編碼方式。對整型int,pb使用了varints編碼方式,varints編碼的優勢是使用了更少的bytes來表示很小的int型別值。

varints編碼方式中,每個byte的最高位bit有特殊含義,如果為1,表示後續的byte也是這個數字的一部分;如果為0,則表示結束。剩餘的7個bit用於表示資料。數字300用varints編碼方式表示為:

1010

1100

0000

0010

由varints編碼規則,去掉第乙個byte的最高位1,去掉第二個byte的最高位0,則有:

1010

1100

0000

0010

→ 010

1100

0000010

varints位元組序使用little-endian,以上數字用big-endian並轉換成10進製有:

pb編碼中,資料以key-value的形式表示,第乙個byte即為key。以上**中不同資料型別對應指定type值,假設message中各字段的數字標識為tag,則key、type和tag有以下對應關係:

key = tag << 3 | type
即key的最後3個bit用於儲存type,有了這層關係,我們試著演算pb中對int和string的編碼。

假設我們截獲到以下pb資料:

08

9601

這段資料具體表示什麼?我們用以上對應關係演算一下,首先該資料key是08,二進位制表示即:

0000

1000

最後3個bit表示type,即type為0(varint格式資料),左移3位得到tag值為1。有了這些資訊,我們可以知道這個資料應該是這樣定義的:

message test1
繼續地,我們用varint格式來解析 96 01,有以下演算過程:

96

01 = 1001

0110

0000

0001

→ 000

0001 ++ 001

0110 (丟棄最高位的bit並轉為big-endian)

→ 10010110

→ 2 + 4 + 16 + 128 = 150

因此我們可以知道這段資料表示150這個數。

12

0774

6573

7469 6e 67

同樣套用以上關係,key是12,二進位制表示即:

0001

0010

最後3個bit表示type,即type為2(length-delimited),左移3位得到tag值為2。有了這些資訊,我們知道這個資料可能是這樣定義的:

message test2
資料型別具體是string、bytes或其他,這並不影響我們解析這段資料,對於length-delimited格式資料,第2個byte表示資料長度(len),對應以上編碼即len為7,這實質是tlv編碼格式。

後續的7個bytes表示有效的傳輸資料,為utf-8編碼下的"testing"字串。

小結

以上介紹了通訊協議 - google protocol buffers,了解了其基本使用方法和編碼方式。pb支援前向相容,可以在不修改client/server程式的情況下修改其中一端的資料格式,在各種rpc框架中經常可以看到它的身影。

reference: protocol buffers developer guide

protocol buffers encoding

通訊協議 UDP通訊

通訊協議 通訊協議是udp tcp通訊的基礎,沒有通訊協議的 通訊 是沒有任何意義上的,通訊協議不是底層函式或者高深的原理,而是程式設計師之間的一種文字約定。資料在網路上是以位元組的形式傳送的 底層是01碼 我們在讀取到資料之後,該以怎樣的方式對它進行解碼呢?這就是我們在通訊之前需要制定的通訊協議。...

串列埠通訊 通訊協議

我們的串列埠程式,除了通用的,進行串列埠監聽收發的簡單工具,大多都和下位機有關,這就需要關心我們的通訊協議如何快取,分析,以及通知介面。所謂通訊協議是指通訊雙方的一種約定。約定包括對資料格式 同步方式 傳送速度 傳送步驟 檢糾錯方式以及控制字元定義等問題做出統一規定,通訊雙方必須共同遵守。因此,也叫...

通訊協議 IIC通訊

iic匯流排是由資料線sda和時鐘線scl構成的序列匯流排,可傳送和接收資料。在匯流排上一般有幾個主機 也可以多個 和多個從機。從機一般不主動傳送資料,主機傳送資料,資料報中包含有從機位址,主機通過從機位址對從機進行操作。1.空閒狀態 時鐘線和資料線都為高電平 稱為釋放匯流排 時鐘線scl由主機控制...