Proto3序列化協議

Tangocc發表於2020-10-15

Proto3序列化協議

簡介

對於網際網路應用來說,客戶端-客戶端、客戶端-服務端之間需要資料的互動,其資料傳輸是二進位制流的方式在網際網路上傳輸,因為需要一種手段將資料物件編碼為一種可以在網路上傳輸的二進位制流,這個過程就叫做序列化。同樣的,客戶端在收到二進位制流需要解碼出資料,這個過程叫反序列。

簡單理解就是序列化就是按照某種編碼方式將資料轉換為二進位制流以方便在網路上傳輸,同時這種編碼方式是可逆的和可理解的。

按照上述定義不難理解,json也是一種序列化的一種方式,將物件編碼為字串(二進位制)進行傳輸。

同樣的proto3是google推出的一種序列化框架。全稱是Protocol Buffers。

其具有以下特點:

  • 小:生成的位元組流採用了各種壓縮方式,相對xml和json這類檔案更小。

  • 快:編解碼基本都是位運算,也沒有複雜的巢狀關係,速度快。

  • 安全:這裡的安全,是指protobuf沒有把欄位名寫入到位元組流裡,只是寫入了欄位號資訊。另外,相對於xml和json來說,因為被編碼成二進位制,破解成本增大。

編碼方式

我們定義了一個Person的訊息型別,那protobuf是怎麼把它編碼成二進位制的呢?其實它是把message轉成一系列的key-value,key就是欄位號,value就是欄位值,大概這樣子存:

message Person {
    string name = 1;
    int32  id   = 2;
}

=1,=2稱為tag, 唯一標識每個元素,其值不可重複,其中tag在1-15之間編碼為一個位元組,大於15位元組將需要多個位元組進行編碼,因此作為優化手段,1-15 tag多使用者經常出現的元素,15+的tag用於不長出現或者可選的元素,此外,對於repeated型別的message,其每個元素都需要一個tag,所以repeated適合用小於15的tag。

對於上述的定義,其會按照[tag1][value1][tag2][value2][tag3][value3]...的方式進行編碼,

解碼時,會從左往右解析每一個key-value,假如遇到某個key-value無法解析了,那麼就直接跳過,不會影響到其它key-value的解析,因此如果你加了新欄位,生成位元組流,然後用舊版本解析,這時它還是能夠解析出舊版本的欄位的,新欄位只是被忽略而已,這就是protobuf的向後相容。

需要注意,一旦釋出proto協議,其tag值就不能修改,新增欄位只能使用未使用的tag(即使刪除的tag也不可以),刪除一個欄位可以,解碼時如果沒有該欄位會預設填充預設值,如果刪除刪除一個欄位,最好採用reversed標識使用過的tag值,避免後續誤使用舊的tag值。

官方建議:

  • xxx.proto一行最多80字元

  • 2個空格縮排

  • 字串用雙引號

  • 檔名小寫、_組成,如lower_snake_case.proto

  • message 定義要駝峰式且首字母大寫

message SongServiceRequest {
  required string song_name = 1;
}

  • 如果成員變數包含陣列,應採用 song_name1 而不是 song_name_1

  • 對於repeated型別應該用複數,如repeated string keys = 1;

//型別是大寫+駝峰式
enum FooBar {
  FOO_BAR_UNSPECIFIED = 0;  // 0值定義UNSPECIFIED
  FOO_BAR_FIRST_VALUE = 1;  //值是全大寫常量定義
  FOO_BAR_SECOND_VALUE = 2;
}

  • 對於Services定義
  //駝峰+大寫
  service FooService {
      rpc GetSomething(FooRequest) returns (FooResponse); //函式名以駝峰+首字母大寫
  }

相關文章