Protobuf3隨筆(一)定義一個消息類型

先來看一個非常簡單的例子。假設你想定義一個“搜索請求”的消息格式,每一個請求含有一個查詢字符串、你感興趣的查詢結果所在的頁數,以及每一頁多少條查詢結果。可以採用如下的方式來定義消息類型的.proto文件了:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 文件的第一行指定了你正在使用proto3語法:如果你沒有指定這個,編譯器會使用proto2。這個指定語法行必須是文件的非空非註釋的第一個行。
  • SearchRequest消息格式有3個字段,在消息中承載的數據分別對應於每一個字段。其中每個字段都有一個名字和一種類型。

1、指定字段類型

在上面的例子中,所有字段都是標量類型:兩個整型(page_number和result_per_page),一個string類型(query)。當然,你也可以爲字段指定其他的合成類型,包括枚舉(enumerations)或其他消息類型。

2、分配標識號

正如你所見,在消息定義中,每個字段都有唯一的一個數字標識符。這些標識符是用來在消息的二進制格式中識別各個字段的,一旦開始使用就不能夠再改變。注:[1,15]之內的標識號在編碼的時候會佔用一個字節。[16,2047]之內的標識號則佔用2個字節。所以應該爲那些頻繁出現的消息元素保留 [1,15]之內的標識號。切記:要爲將來有可能添加的、頻繁出現的標識號預留一些標識號。

最小的標識號可以從1開始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (從FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的標識號, Protobuf協議實現中對這些進行了預留。如果非要在.proto文件中使用這些預留標識號,編譯時就會報警。同樣你也不能使用早期保留的標識號。

3、指定字段規則

所指定的消息字段修飾符必須是如下之一:

  • singular:一個格式良好的消息應該有0個或者1個這種字段(但是不能超過1個)。
  • repeated:在一個格式良好的消息中,這種字段可以重複任意多次(包括0次)。重複的值的順序會被保留。

在proto3中,repeated的標量域默認情況蝦使用packed。

你可以瞭解更多的pakced屬性在Protocol Buffer 編碼

4、添加更多消息類型

在一個.proto文件中可以定義多個消息類型。在定義多個相關的消息的時候,這一點特別有用——例如,如果想定義與SearchResponse消息類型對應的回覆消息格式的話,你可以將它添加到相同的.proto文件中,如:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
message SearchResponse {
 ...
}

5、添加註釋

向.proto文件添加註釋,可以使用C/C++/Java風格的雙斜槓(//) 語法格式,如:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

6、保留標識符(Reserved)

如果你通過刪除或者註釋所有域,以後的用戶在更新這個類型的時候可能重用這些標識號。如果你使用舊版本加載相同的.proto文件會導致嚴重的問題,包括數據損壞、隱私錯誤等等。現在有一種確保不會發生這種情況的方法就是爲字段tag(reserved name可能會JSON序列化的問題)指定reserved標識符,protocol buffer的編譯器會警告未來嘗試使用這些域標識符的用戶。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

注:不要在同一行reserved聲明中同時聲明域名字和tag number。

7、從.proto文件生成了什麼?

當用protocol buffer編譯器來運行.proto文件時,編譯器將生成所選擇語言的代碼,這些代碼可以操作在.proto文件中定義的消息類型,包括獲取、設置字段值,將消息序列化到一個輸出流中,以及從一個輸入流中解析消息。

  • 對C++來說,編譯器會爲每個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每一個消息有一個對應的類。
  • 對Java來說,編譯器爲每一個消息類型生成了一個.java文件,以及一個特殊的Builder類(該類是用來創建消息類接口的)。
  • 對Python來說,有點不太一樣——Python編譯器爲.proto文件中的每個消息類型生成一個含有靜態描述符的模塊,,該模塊與一個元類(metaclass)在運行時(runtime)被用來創建所需的Python數據訪問類。
  • 對go來說,編譯器會位每個消息類型生成了一個.pd.go文件。
  • 對於Ruby來說,編譯器會爲每個消息類型生成了一個.rb文件。
  • javaNano來說,編譯器輸出類似域java但是沒有Builder類
  • 對於Objective-C來說,編譯器會爲每個消息類型生成了一個pbobjc.h文件和pbobjcm文件,.proto文件中的每一個消息有一個對應的類。
  • 對於C#來說,編譯器會爲每個消息類型生成了一個.cs文件,.proto文件中的每一個消息有一個對應的類。
    你可以從如下的文檔鏈接中獲取每種語言更多API(proto3版本的內容很快就公佈)。API Reference
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章