Potocol Buffer詳解

protocol安裝及使用

上一篇博文介紹了一個綜合案例,這篇將詳細介紹protocol buffer。html

爲何使用protocol buffer?

  1. java默認序列化效率較低。
  2. apache的thrift方案並沒有明顯優點,可是使用成本較高,安裝等較爲麻煩。
  3. 雖然PB不支持map,可是咱們的應用中map用的較少,即便使用了存儲的數據量也較少,能夠轉化爲list方案進行存儲。
  4. 之前我玩過PB,這很關鍵,當你有某方面的經驗時能較輕鬆的應對一些意外狀況。
  5. 保證同一消息報文新舊版本之間的兼容性
  6.  使用SOAP協議(WebService)做爲消息報文的格式載體,由該方式生成的報文是基於文本格式的,同時還存在大量的XML描述信息,所以將會大大增長網絡IO的負擔。

怎麼安裝?

首先,要使用protocol buffer得保證maven安裝成功,maven的下載地址:http://maven.apache.org/download.cgi 。java

1.解壓完以後請將maven的bin目錄配置到你的環境變量當中。python

2.請確保你的JAVA_HOME的變量是指向你的JDK的主目錄,若是你的系統變量中沒有JAVA_HOME這一項,請點擊新建添加。apache

3.打開命令行,輸入「mvn --version」若是輸出正確則表示安裝成功編程

安裝完maven以後就要進行protocol buffer的安裝了,下載地址: http://code.google.com/p/protobuf/downloads/list 。下載protobuf-2.4.1.zip 和 protoc-2.4.1-win32.zip 兩個包。windows

1. 解壓完成以後有兩種選擇,第一:將protoc-2.4.1-win32中的protoc.exe所在的目錄配置到環境變量當中,第二:將protoc.exe拷貝到c:\windows\system32目錄下,這裏推薦第二種作法。數組

2. 將proto.exe文件拷貝到解壓後的protobuf-2.4.1\src目錄中.網絡

3. 進入protobuf-2.4.1\java 目錄  執行mvn package命令編輯該包,系統將會在target目錄中生成protobuf-java-2.4.1.jar文件(注意運行時須要聯網,首次安裝可能須要必定的時間)。數據結構

4. 假設你的數據文件目錄在XXX\data目錄,把上一步生成的jar拷貝到該目錄中便可。eclipse

5. 進入XXX\protobuf-2.4.1\examples目錄,能夠看到addressbook.proto文件,在命令行中執行 protoc --java_out=. addressbook.proto 命令(特別注意. Addressbook.proto中間的空格,我第一次安裝就由於沒注意而反覆失敗),若是生成com文件夾而且最終生成AddressBookProtos類則說明安裝成功。

6. 打開eclipse,選擇windows-->preferences-->java-->Installed JREs編輯你默認的java源碼包,並將上面所提到的protobuf-java-2.4.1.jar文件添加進去。

以上內容均摘抄與網絡,經驗證可正確安裝

Protocol Buffer(語言規範)

如下文章摘抄http://www.cnblogs.com/stephen-liu74/archive/2013/01/02/2841485.html

一.Protobuf 的優勢

Protobuf 有如 XML,不過它更小、更快、也更簡單。你能夠定義本身的數據結構,而後使用代碼生成器生成的代碼來讀寫這個數據結構。你甚至能夠在無需從新部署程序的狀況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,便可利用各類不一樣語言或從各類不一樣數據流中對你的結構化數據輕鬆讀寫。

它有一個很是棒的特性,即「向後」兼容性好,人們沒必要破壞已部署的、依靠「老」數據格式的程序就能夠對數據結構進行升級。這樣您的程序就能夠沒必要擔憂由於消息結構的改變而形成的大規模的代碼重構或者遷移的問題。由於添加新的消息中的 field 並不會引發已經發布的程序的任何改變。

Protobuf 語義更清晰,無需相似 XML 解析器的東西(由於 Protobuf 編譯器會將 .proto 文件編譯生成對應的數據訪問類以對 Protobuf 數據進行序列化、反序列化操做)。

使用 Protobuf 無需學習複雜的文檔對象模型,Protobuf 的編程模式比較友好,簡單易學,同時它擁有良好的文檔和示例,對於喜歡簡單事物的人們而言,Protobuf 比其餘的技術更加有吸引力。

 2、定義第一個Protocol Buffer消息。
      建立擴展名爲.proto的文件,如:MyMessage.proto,並將如下內容存入該文件中。
      message LogonReqMessage {
          required int64 acctID = 1;
          required string passwd = 2;
      }
      這裏將給出以上消息定義的關鍵性說明。
      1. message是消息定義的關鍵字,等同於C++中的struct/class,或是Java中的class。
      2. LogonReqMessage爲消息的名字,等同於結構體名或類名。
      3. required前綴表示該字段爲必要字段,既在序列化和反序列化以前該字段必須已經被賦值。與此同時,在Protocol Buffer中還存在另外兩個相似的關鍵字,optional和repeated,帶有這兩種限定符的消息字段則沒有required字段這樣的限制。相比於optional,repeated主要用於表示數組字段。具體的使用方式在後面的用例中均會一一列出。
      4. int64和string分別表示長整型和字符串型的消息字段,在Protocol Buffer中存在一張類型對照表,既Protocol Buffer中的數據類型與其餘編程語言(C++/Java)中所用類型的對照。該對照表中還將給出在不一樣的數據場景下,哪一種類型更爲高效。該對照表將在後面給出。
      5. acctID和passwd分別表示消息字段名,等同於Java中的域變量名,或是C++中的成員變量名。
      6. 標籤數字12則表示不一樣的字段在序列化後的二進制數據中的佈局位置。在該例中,passwd字段編碼後的數據必定位於acctID以後。須要注意的是該值在同一message中不能重複。另外,對於Protocol Buffer而言,標籤值爲1到15的字段在編碼時能夠獲得優化,既標籤值和類型信息僅佔有一個byte,標籤範圍是16到2047的將佔有兩個bytes,而Protocol Buffer能夠支持的字段數量則爲2的29次方減一。有鑑於此,咱們在設計消息結構時,能夠儘量考慮讓repeated類型的字段標籤位於1到15之間,這樣即可以有效的節省編碼後的字節數量。

      3、定義第二個(含有枚舉字段)Protocol Buffer消息。
      //在定義Protocol Buffer的消息時,可使用和C++/Java代碼一樣的方式添加註釋。
      enum UserStatus {
          OFFLINE = 0;  //表示處於離線狀態的用戶
          ONLINE = 1;   //表示處於在線狀態的用戶
      }
      message UserInfo {
          required int64 acctID = 1;
          required string name = 2;
          required UserStatus status = 3;
      }
      這裏將給出以上消息定義的關鍵性說明(僅包括上一小節中沒有描述的)。
      1. enum是枚舉類型定義的關鍵字,等同於C++/Java中的enum。
      2. UserStatus爲枚舉的名字。
      3. 和C++/Java中的枚舉不一樣的是,枚舉值之間的分隔符是分號,而不是逗號。
      4. OFFLINE/ONLINE爲枚舉值。
      5. 0和1表示枚舉值所對應的實際整型值,和C/C++同樣,能夠爲枚舉值指定任意整型值,而無需老是從0開始定義。如:
      enum OperationCode {
          LOGON_REQ_CODE = 101;
          LOGOUT_REQ_CODE = 102;
          RETRIEVE_BUDDIES_REQ_CODE = 103;
    
          LOGON_RESP_CODE = 1001;
          LOGOUT_RESP_CODE = 1002;
          RETRIEVE_BUDDIES_RESP_CODE = 1003;
      }

      4、定義第三個(含有嵌套消息字段)Protocol Buffer消息。
      咱們能夠在同一個.proto文件中定義多個message,這樣即可以很容易的實現嵌套消息的定義。如:
      enum UserStatus {
          OFFLINE = 0;
          ONLINE = 1;
      }
      message UserInfo {
          required int64 acctID = 1;
          required string name = 2;
          required UserStatus status = 3;
      }
      message LogonRespMessage {
          required LoginResult logonResult = 1;
          required UserInfo userInfo = 2;
      }
      這裏將給出以上消息定義的關鍵性說明(僅包括上兩小節中沒有描述的)。
      1. LogonRespMessage消息的定義中包含另一個消息類型做爲其字段,如UserInfo userInfo。
      2. 上例中的UserInfo和LogonRespMessage被定義在同一個.proto文件中,那麼咱們是否能夠包含在其餘.proto文件中定義的message呢?Protocol Buffer提供了另一個關鍵字import,這樣咱們即可以將不少通用的message定義在同一個.proto文件中,而其餘消息定義文件能夠經過import的方式將該文件中定義的消息包含進來,如:
      import "myproject/CommonMessages.proto"

     5、限定符(required/optional/repeated)的基本規則。
      1. 在每一個消息中必須至少留有一個required類型的字段。 
      2. 每一個消息中能夠包含0個或多個optional類型的字段。
      3. repeated表示的字段能夠包含0個或多個數據。須要說明的是,這一點有別於C++/Java中的數組,由於後二者中的數組必須包含至少一個元素。
      4. 若是打算在原有消息協議中添加新的字段,同時還要保證老版本的程序可以正常讀取或寫入,那麼對於新添加的字段必須是optional或repeated。道理很是簡單,老版本程序沒法讀取或寫入新增的required限定符的字段。

      6、類型對照表。

.proto Type Notes C++ Type Java Type
double    double  double
float    float  float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.  int32  int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – 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


      7、Protocol Buffer消息升級原則。
      在實際的開發中會存在這樣一種應用場景,既消息格式由於某些需求的變化而不得不進行必要的升級,可是有些使用原有消息格式的應用程序暫時又不能被馬上升級,這便要求咱們在升級消息格式時要遵照必定的規則,從而能夠保證基於新老消息格式的新老程序同時運行。規則以下:
      1. 不要修改已經存在字段的標籤號。
      2. 任何新添加的字段必須是optional和repeated限定符,不然沒法保證新老程序在互相傳遞消息時的消息兼容性。
      3. 在原有的消息中,不能移除已經存在的required字段,optional和repeated類型的字段能夠被移除,可是他們以前使用的標籤號必須被保留,不能被新的字段重用。
      4. int3二、uint3二、int6四、uint64和bool等類型之間是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32和sfixed32,以及fixed64和sfixed64之間是兼容的,這意味着若是想修改原有字段的類型時,爲了保證兼容性,只能將其修改成與其原有類型兼容的類型,不然就將打破新老消息格式的兼容性。
      5. optional和repeated限定符也是相互兼容的。

      8、Packages。
      咱們能夠在.proto文件中定義包名,如:
      package ourproject.lyphone;
      該包名在生成對應的C++文件時,將被替換爲名字空間名稱,既namespace ourproject { namespace lyphone。而在生成的Java代碼文件中將成爲包名。

      9、Options。
      Protocol Buffer容許咱們在.proto文件中定義一些經常使用的選項,這樣能夠指示Protocol Buffer編譯器幫助咱們生成更爲匹配的目標語言代碼。Protocol Buffer內置的選項被分爲如下三個級別:
      1. 文件級別,這樣的選項將影響當前文件中定義的全部消息和枚舉。
      2. 消息級別,這樣的選項僅影響某個消息及其包含的全部字段。
      3. 字段級別,這樣的選項僅僅響應與其相關的字段。
      下面將給出一些經常使用的Protocol Buffer選項。
      1. option java_package = "com.companyname.projectname";
      java_package是文件級別的選項,經過指定該選項可讓生成Java代碼的包名爲該選項值,如上例中的Java代碼包名爲com.companyname.projectname。與此同時,生成的Java文件也將會自動存放到指定輸出目錄下的com/companyname/projectname子目錄中。若是沒有指定該選項,Java的包名則爲package關鍵字指定的名稱。該選項對於生成C++代碼毫無影響。
      2. option java_outer_classname = "LYPhoneMessage";
      java_outer_classname是文件級別的選項,主要功能是顯示的指定生成Java代碼的外部類名稱。若是沒有指定該選項,Java代碼的外部類名稱爲當前文件的文件名部分,同時還要將文件名轉換爲駝峯格式,如:my_project.proto,那麼該文件的默認外部類名稱將爲MyProject。該選項對於生成C++代碼毫無影響。
      注:主要是由於Java中要求同一個.java文件中只能包含一個Java外部類或外部接口,而C++則不存在此限制。所以在.proto文件中定義的消息均爲指定外部類的內部類,這樣才能將這些消息生成到同一個Java文件中。在實際的使用中,爲了不老是輸入該外部類限定符,能夠將該外部類靜態引入到當前Java文件中,如:import static com.company.project.LYPhoneMessage.*
      3. option optimize_for = LITE_RUNTIME;
      optimize_for是文件級別的選項,Protocol Buffer定義三種優化級別SPEED/CODE_SIZE/LITE_RUNTIME。缺省狀況下是SPEED。
      SPEED: 表示生成的代碼運行效率高,可是由今生成的代碼編譯後會佔用更多的空間。
      CODE_SIZE: 和SPEED偏偏相反,代碼運行效率較低,可是由今生成的代碼編譯後會佔用更少的空間,一般用於資源有限的平臺,如Mobile。
      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。    
      4. [pack = true]: 由於歷史緣由,對於數值型的repeated字段,如int3二、int64等,在編碼時並無獲得很好的優化,然而在新近版本的Protocol Buffer中,可經過添加[pack=true]的字段選項,以通知Protocol Buffer在爲該類型的消息對象編碼時更加高效。如:
      repeated int32 samples = 4 [packed=true]。
      注:該選項僅適用於2.3.0以上的Protocol Buffer。
      5. [default = default_value]: optional類型的字段,若是在序列化時沒有被設置,或者是老版本的消息中根本不存在該字段,那麼在反序列化該類型的消息是,optional的字段將被賦予類型相關的缺省值,如bool被設置爲false,int32被設置爲0。Protocol Buffer也支持自定義的缺省值,如:
      optional int32 result_per_page = 3 [default = 10]。

     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。

相關文章
相關標籤/搜索