Protocol Buffer 簡介
-
Google Protocol Buffer(簡稱Protobuf)是Google公司內部的混合語言數據標準,是一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,或者說序列化。是一種可用於通訊協議、數據存儲等領域的無關語言、無關平臺、可擴展的序列化結構數據格式。
-
通過將結構化數據串行化(序列化),從而實現數據存儲 / RPC數據交換的功能。
- 序列化:將數據結構或對象轉化爲二進制串的過程;
- 反序列化:將在序列化過程中生成的二進制串轉化成數據結構或對象的過程。
序列化協議需要考慮些什麼?
- 序列化之後的流大小(佔用網絡寬帶)
- 序列化和反序列化的性能(CPU+內存等資源佔用)
- 是否支持跨語言
protobuf特點
-
性能方面
- 體積小。序列化後,數據大小均縮小約3倍;
- 序列化速度快,比XML和Json快2~50倍;
- 傳輸速度快,因爲體積小,傳輸起來帶寬和速度會有優化;
-
使用方面
- 使用簡單,protobuf編譯器自動進行序列化和反序列化
- 維護成本低,多平臺僅需要維護一份對象協議文件。
- 向後兼容好,即可擴展性好,不必破壞就數據格式就可以直接對數據格式進行更新;
- 加密性好,HTTP傳輸抓包只能抓到序列後的的二進制串。
-
使用範圍
- 跨平臺
- 跨語言
- 高可擴展性
protobuf的缺點
- 不適用於對基於文本的標記文檔(如HTML)建模,因爲文本不適合描述數據結構;
- 通用性差,protobuf只是Google公司內部使用的工具,沒有被大衆化;
- 以二進制數據流方式進行存儲(不可讀),需要通過proto文件才能瞭解數據結構。
指定字段規則
-
require: 格式良好的message必須包含該字段一次;
-
optional: 格式良好的message必須包含該字段零次或者一次;
-
repeated: 該字段可在格式良好的消息中重複任意多次(包括零次),其中重複的順序會被保留。(由於一些歷史原因,標量數字類型的 repeated 字段不能儘可能高效地編碼。新代碼應使用特殊選項 [packed = true] 來獲得更高效的編碼。)
repeated int32 samples = 4 [packed=true]; //注意:對 required 的使用永遠都應該非常小心。如果你希望在某個時刻停止寫入或發送 required 字段, 則將字段更改爲可選字段將會有問題 - 舊讀者會認爲沒有此字段的郵件不完整,可能會無意中拒 絕或刪除它們。你應該考慮爲 buffers 編寫特定於應用程序的自定義驗證的例程。谷歌的一些工 程師得出的結論是,使用 required 弊大於利;他們更喜歡只使用 optional 和 repeated。但是, 這種觀點並未普及。
Reserved保留字段
-
如果你通過完全刪除或註釋來更新message類型,若未來的一些用戶在做更新或修改時就可能再次使用到這些編號如果以後加載相同
.proto
的舊版本,這可能會導致一些嚴重問題,包括數據損壞,隱私錯誤等。確保不會發生這種情況的一種方法是指定已刪除字段的字段編號(有時也需要指定名稱爲保留狀態,英文名稱可能會導致 JSON 序列化問題)爲 “保留” 狀態。如果將來的任何用戶嘗試使用這些字段標識符,protocol buffer 編譯器將會抱怨。message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
導入定義 importing definitions
-
若想要使用另外一個
.proto
文件中的message定義,可以通過在文件頂部添加一個import
語句來對文件進行導入。import "myproject/other_protos.proto";
-
默認情況下,你只能使用直接導入的 .proto 文件中的定義。但是,有時你可能需要將 .proto 文件移動到新位置。現在,你可以在舊位置放置一個虛擬 .proto 文件,以使用 import public 概念將所有導入轉發到新位置,而不是直接移動 .proto 文件並在一次更改中更新所有調用點。導入包含 import public 語句的 proto 的任何人都可以傳遞依賴導入公共依賴項。
// new.proto // All definitions are moved here // old.proto // This is the proto that all clients are importing. import public "new.proto"; import "other.proto"; // client.proto import "old.proto"; // 你可以使用 old.proto 和 new.proto 中的定義,但無法使用 other.proto
更新 message 類型規則
- 請勿更改任何現有字段的字段編號;
- 新添加的任何字段都應該是 optional 或 repeated 字段;
- 只要在更新的 message 類型中不再使用字段編號,就可以刪除非必填字段;
- int32,uint32,int64,uint64 和 bool 都是兼容的 。 這意味着你可以將字段從這些類型更改爲另一種類型,而不會破壞向前或向後兼容性;
- sint32 和 sint64 彼此兼容,但與其他整數類型不兼容;
- 只要字節是有效的 UTF-8,string 和 bytes 就是兼容的;
- 如果字節包含 message 的編碼版本,則嵌入 message 與 bytes 兼容;
- fixed32 與 sfixed32 兼容,fixed64 與 sfixed64 兼容;
- optional 與 repeated 兼容;
- 更改默認值通常是正常的,只要你記住永遠不會通過網絡發送默認值。因此,如果程序接收到未設置特定字段的消息,則程序將看到該程序的協議版本中定義的默認值。它不會看到發件人代碼中定義的默認值;
- enum 與 int32,uint32,int64 和 uint64兼容(注意,如果它們不適合,值將被截斷),但要注意 message 反序列化時客戶端代碼對待它們將有所不同。值得注意的是,當 message 被反序列化時,將丟棄無法識別的 enum 值,這使得字段的 has… 訪問器返回 false 並且其 getter 返回 enum 定義中列出的第一個值,或者如果指定了一個默認值則返回默認值。在 repeated 枚舉字段的情況下,任何無法識別的值都將從列表中刪除。但是,整數字段將始終保留其值。因此,在有可能接收超出範圍的枚舉值時,對整數升級爲 enum 這一操作需要非常小心;
- 將單個 optional 值更改爲 newoneof 的成員是安全且二進制兼容的。如果你確定沒有代碼一次設置多個,則將多個 optional 字段移動到新的 oneof 中可能是安全的。但是將任何字段移動到現有的 oneof 是不安全的。
定義一個protocol buffer message 類型
//簡易的博客系統定義proto字段
/*
*1.文章屬性:ID、標題、描述、正文、標籤、是否指定、創建時間、創建作者
*2.標籤類別:最熱、PHP、最新
*3.作者屬性:ID、名稱、描述
*/
message BlogSystem{
enum TagType{
HOT = 1;
PHP = 2;
NEW = 3;
}
message AuthorMessage{
required int32 id = 1;
required string name = 2;
optional string introduce = 3;
}
required int32 id = 1;
required string title = 2;
optional string description = 3;
required string content = 4;
required TagType tag = 5;
optional bool stick = 6;
required string createtime = 7;
required AuthorMessage author = 8;
}