序列化和反序列化及Protobuf 基本使用


序列化和反序列化##

序列化和反序列化在平常工作中會大量使用,然而並不一定非常清楚它的概念。序列化和反序列化的選型卻是系統設計或重構一個重要的環節,在分布式、大數據量系統設計里面更為顯著。機器間的通信需要約定一個協議,序列化和反序列化是這個通信協議的一部分。
序列化:將對象或數據結構轉為字節序列的過程。
反序列化:將序列化后生成的字節序列轉為對象或數據結構的過程。

常用序列化和反序列化組件##

比較常見的序列化和反序列化組方式有XML、JSON和Protobuf等。XML標准化較早,基於XML的SOAP是一種應用廣泛的結構化數據傳遞協議。JSON源於js,較之XML它更小、解析更快,而且同樣具備可讀性好的優點。而源於谷歌的protobuf現在在大型分布式系統廣泛使用。

Protobuf##

Protobuf是谷歌推出的一款平台無關,語言無關,可擴展的序列化和反序列化技術。

字段規則

要使用Protobuf,首先需要定義.proto文件
如下proto2中:

message msg
{
    required int32 a=1;
    optional string b=2;
    repeated string c=3;
}

其中:

  • message是消息定義的關鍵字。
  • required 表示這個字段必須的,必須在序列化的時候被賦值。
  • optional 代表這個字段是可選的,可以為0個或1個但不能大於1個。
  • repeated 則代表此字段可以被重復任意多次包括0次。類似C++ STL中的vector。
  • int32和string是字段的類型。后面是我們定義的字段名。
  • 最后的1,2,3則是代表每個字段的一個唯一的編號標簽,在同一個消息里不可以重復。這些編號標簽用與在消息二進制格式中標識你的字段,並且消息一旦定義就不能更改。需要說明的是標簽在1到15范圍的采用一個字節進行編碼。所以通常將標簽1到15用於頻繁發生的消息字段。編號標簽大小的范圍是1到\(2^{29}-1\)。此外不能使用protobuf系統預留的編號標簽(19000 -19999)。

而在proto3中,字段規則中去除了required和optional,增加singular。但是proto3仍兼容proto2

message msg
{
    int32 a=1;
    singular string b=2;
    repeated string c=3;
}

其中,

  • singular:一個格式良好的消息應該有0個或者1個這種字段(但是不能超過1個)。
  • repeated
  • 在proto3中,repeated的標量域默認情況下使用packed。

一個較完整的.proto文件

syntax = "proto3";
message Article {
  int32 article_id = 1;
  singular string article_excerpt = 2;
  repeated string article_picture = 3;
  singular int32  article_pagecount = 4 [default = 0];
  enum ArticleType {
    NOVEL = 0;
    PROSE = 1;
    PAPER = 2;
    POETRY = 3;
  }
  singular ArticleType article_type = 5 [default = NOVEL];
  message Author {
    string name = 1; 
    singular string phone = 2;
  }
  singular Author author = 6;
  repeated int32 article_numberofwords = 7 [packed=true];
  reserved  9, 10, 12 to 15;
  extensions 100 to 1000;
}

extend Article {
  singular int32 followers_count = 101;
  singular int32 likes_count= 102;
}

message Other {
  singular string other_info = 1;
  oneof test_oneof {
    string code1 = 2;
    string code2 = 3;
  }
}
  • 上面proto文件,我們定義了enum枚舉類型,嵌套的消息。甚至對原有的消息進行了擴展,也可以對字段設置默認值。添加注釋等,類似C++注釋。
  • 此外reserved關鍵字主要用於保留相關編號標簽,主要是防止在更新proto文件刪除了某些字段,而未來的使用者定義新的字段時重新使用了該編號標簽。這會引起一些問題在獲取老版本的消息時,譬如數據沖突,隱藏的一些bug等。所以一定要用reserved標記這些編號標簽以保證不會被使用。
  • 當我們需要對消息進行擴展的時候,我們可以用extensions關鍵字來定義一些編號標簽供第三方擴展。這樣的好處是不需要修改原來的消息格式。就像上面proto文件,我們用extend關鍵字來擴展。只要擴展的字段編號標簽在extensions定義的范圍里。
  • 對於基本數值類型,由於歷史原因,不能被protobuf更有效的encode。所以在新的代碼中使用packed=true可以更加有效率的encode。注意packed只能用於repeated 數值類型的字段。不能用於string類型的字段。
  • 在消息Other中我們看到定義了一個oneof關鍵字。這個關鍵字作用比較有意思。當你設置了oneof里某個成員值時,它會自動清除掉oneof里的其他成員,也就是說同一時刻oneof里只有一個成員有效。這常用於你有許多optional字段時但同一時刻只能使用其中一個,就可以用oneof來加強這種效果。但需要注意的是oneof里的字段不能用singular,repeated關鍵字。

導入定義

我們總不能都定義在一個文件中。當一個proto文件需要另一個proto文件的時候,我們可以通過import導入。protobuf也提供了包的定義,只要在文件開頭定義package關鍵字即可。

import "test.proto"
package foo.bar;

編譯問題

針對不同語言,依據.proto文件編譯成我們需要的語言文件。如C++下

protoc -I=SRC_DIR --cpp_out=DST_DIR SRC_DIR/ex.proto

先簡單記錄這些。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM