protobuf 語法淺析

Protobuf

爲何用Protobuf Buffer

跨語言平臺編程,使用SOAP的話,該方式是使用xml的方式傳輸,會大大增長網絡的IO,並且xml的解析複雜,下降報文的解析性能。java

定義一個Protobuf消息

message LogonReqMessage{
	required int64 actID=1;
	required string passwd=2;
}

關鍵說明:python

  1. message是消息的關鍵字
  2. LogonReqMessage是消息名字,至關於java的類名
  3. required前綴表示該字段未必要字段,序列化先後必須賦值的字段。protobuf還存在兩個相似的關鍵字optionalrepeated主要用於表示數組字段。
  4. int64和string表示長整型和字符串類型的消息字段,在protobuf中存在一張類型對照表,即protobuf類型與其餘語言的類型對照。
  5. actID和passwd分別表示消息的字段名,等同於java中的域名變量名。
  6. 標籤數字12表示不一樣的字段在序列化先後的二進制中的佈局位置。在本例中,passwd字段編碼後的數據必定位於actID後,該值在同一個message中不能重複。對於protobuf而言,標籤1到15的字段在編碼的時候是能夠獲得優化的(標籤值和類型信息只佔一個byte,標籤範圍16到2047佔兩個byte),protobuf可支持的字段數量是$2^{29}-1$。所以,應該將repeated類型的字段標籤未與1~15,節省編碼後的字節數量。

定義第二個protobuf消息

enum UserStatus{
	OFFLINE=0;
	ONLINE=1;
}

message UserInfo{
	required int64 actID=1;
	required string name=2;
	required UserStatus=3;
}

關鍵說明:c++

  1. enum是枚舉類型的關鍵字,等同於java裏面的enum
  2. UserStatus爲枚舉的名字
  3. 和java同樣枚舉之間的分隔符是分好;而不是逗號,
  4. OFFLINEONLINE表示枚舉值
  5. 01表示枚舉所對應的實際整型值,可爲任意整型值,無需從0開始。

定義第三個protobuf消息

enum UserStatus{
	OFFLINE=0;
	ONLINE=1;
}

message UserInfo{
	required int64 actID=1;
	required string name=2;
	required UserStatus=3;
}

message LogonRespMessage{
	required LoginResult logonResult = 1;
	required UserInfo userInfo = 2;
}

關鍵說明:編程

  1. LogonRespMessage消息定義中包含另一個消息類型做爲其字段,如UserInfo userInfo
  2. 上例的UserInfo和LogonRespMessage被定義在同一個.proto文件中,那麼怎麼包含其餘proto文件中的message呢?ProtoBuf提供關鍵字import將其餘proto文件的message引入到當前proto文件中。例如Import "myproject/CommonMessages.proto"

限定符號

  1. 每一個消息必須有一個字段是required類型的字段。
  2. 每一個消息包含0個或多個optional類型的字段。
  3. repeated表示的字段能夠包含0個或多個數據,有別於java的數組,由於java數組中至少包含一個元素
  4. 若是打算在原有消息協議中添加新的字段,同時保證老的字段能正常的讀取寫入,那麼新添加的字段必須是optional或者repeated類型的。

proto類型對照表

.proto Type Notes C++ Type Java Type
double double double
float float float
int32 "Uses variable-length encoding. Inefficient for encoding negative numbers ¨C if your field is likely to have negative values use sint32 instead." int32 int
int64 "Uses variable-length encoding. Inefficient for encoding negative numbers ¨C if your field is likely to have negative values use sint64 instead." int64 long
uint32 Uses variable-length encoding. uint32 int
uint64 Uses variable-length encoding. uint64 long
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long
sfixed32 Always four bytes. int32 int
sfixed64 Always eight bytes. int64 long
bool bool boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String
bytes May contain any arbitrary sequence of bytes. string ByteString

protobuf消息升級原則

  1. 不要修改已經存在字段的標籤號
  2. 任何新添加的字段必須是optional或者repeated限定符,不然沒法保證新老程序在互傳消息的時候消息的兼容性。
  3. 在原有消息中,不能移除已經存在的required字段,optionalrepeated類型字段能夠移除,可是他們以前使用的標籤不能使用了。
  4. int32uint32int64uint64bool等類型之間是兼容的,sint32sint64是兼容的,stirngbyte是兼容的,fixed32sfixed32是兼容的,fixed64sfixed64是兼容的,若是想修改原有字段類型,爲了保證兼容性,只能將其修改成原有類型兼容的類型,不然打破新老消息格式的兼容性。
  5. optionalrepeated限定符是相互兼容的。

packages

能夠在.proto文件中定義包名,如:package abc.lypgone,該包名生成c++時,替換成名字空間,而java爲java的包名。數組

options

protobuf 在.proto文件中定義一些經常使用的選項,這樣protobuf能夠幫助咱們生成更匹配的目標語言代碼。網絡

protobuf的內置選項分爲三個等級:工具

  1. 文件級,這樣的選項影響當前文件的全部消息和枚舉。
  2. 消息級,這樣的選項僅影響某個消息及其包含的全部字段。
  3. 字段級,這樣的選項近影響某個字段。

經常使用的protobuf選項有:佈局

  1. option java_package="com.companyname.projectname"; java_package 是文件級的選項,指定讓生成的java代碼的包名爲該選項的值;與此同時,輸出的文件也自動輸出到對應包目錄下(上例的com/companyname/projectname目錄下),該選項對C++沒有影響。
  2. option java_outer_classname="LYPhoneMessage"; java_outer_classname是文件級別的選項,指定生成的java代碼的外部類名稱。若是沒有指定,java代碼的外部類名稱爲當前文件的文件名部分,同時將文件名轉爲駝峯格式,如my_project.proto,那麼該文件的外部類名爲MyProject,該選項對C++代碼無影響。

注意,因爲一個java文件只能有一個外部類或者外部接口,因此.proto文件中的message定義的消息均爲外部類的內部類,這樣才能將這些消息定義到一個文件中。c++沒有此限制。性能

  1. option optimize_for = LITE_RUNTIME; optimize_for 是文件級選項,是protobuf優化選項。該選項又分爲3個等級:
    1. SPEED:表示生成的代碼運行效率高,可是由今生成的代碼編譯後會佔用更多的空間
    2. CODE_SIZE: 和SPEED偏偏相反,代碼運行效率較低,可是由今生成的代碼編譯後會佔用更少的空間,一般用於資源有限的平臺,如Mobile。
    3. LITE_RUNTIME: 生成的代碼執行效率高,同時生成代碼編譯後的所佔用的空間也是很是少。這是以犧牲Protocol Buffer提供的反射功能爲代價的。所以咱們在C++中連接Protocol Buffer庫時僅需連接libprotobuf-lite,而非libprotobuf。在Java中僅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar

對於LITE_MESSAGE選項而言,其生成的代碼均將繼承自MessageLite,而非Message優化

  1. [pack = true]: 由於歷史緣由,對於數值型的repeated字段,如int3二、int64等,在編碼時並無獲得很好的優化,然而在新近版本的Protocol Buffer中,可經過添加[pack=true]的字段選項,以通知Protocol Buffer在爲該類型的消息對象編碼時更加高效。如:repeated int32 samples = 4 [packed=true]

注:該選項僅適用於2.3.0以上的Protocol Buffer

  1. [default = default_value]: optional類型的字段,若是在序列化時沒有被設置,或者是老版本的消息中根本不存在該字段,那麼在反序列化該類型的消息是,optional的字段將被賦予類型相關的缺省值,如bool被設置爲false,int32被設置爲0。Protocol Buffer也支持自定義的缺省值,如:optional int32 result_per_page = 3 [default = 10]。

命令行編譯工具

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

這裏將給出上述命令的參數解釋。

  1. protoc爲Protocol Buffer提供的命令行編譯工具
  2. --proto_path等同於-I選項,主要用於指定待編譯的.proto消息定義文件所在的目錄,該選項能夠被同時指定多個。
  3. --cpp_out選項表示生成C++代碼--java_out表示生成Java代碼--python_out則表示生成Python代碼,其後的目錄爲生成後的代碼所存放的目錄。
  4. path/to/file.proto表示待編譯的消息定義文件

注:對於C++而言,經過Protocol Buffer編譯工具,能夠將每一個.proto文件生成出一對.h和.cc的C++代碼文件。生成後的文件能夠直接加載到應用程序所在的工程項目中。如:MyMessage.proto生成的文件爲MyMessage.pb.h和MyMessage.pb.cc。

相關文章
相關標籤/搜索