Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標準.html
我理解的就是:它是一種輕便高效的結構化數據存儲格式,能夠用於結構化數據串行化,或者說序列化。方便文件的存儲與網絡傳輸.ios
咱們本身就不用定義它們的存儲與傳輸協議了.git
第一步, 寫一個proto的文件 .定義你須要的數據結構.github
每二步, 使用你想要用的語言的proto文件編譯器把寫的proto文件編譯爲目標語言的相關類. (目前google提供了 C++、Java、Python 三種語言的 API).shell
第三步, 把第二步生成的類包含到你寫的程序中, 就可使用它了.ubuntu
看個基於C++主語言的例子,下面是一個.proto文件,網絡
package information // 定義了package的名字 message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
當咱們命名這個文件時,用一個良好的命名習慣: 以這個的格式,packagename.MessageName.proto,因此這裏的話,咱們能夠把這個文件命名爲 information.Person.proto數據結構
而後,咱們把.proto文件用編譯器編譯成C++的代碼之後生成了兩個文件:information.Person.h和information.Person.cc文件.ui
如今咱們就能夠用它了.google
如定義一個Person的類對象,而後設置它裏面的值(經過類的方法),populate,
Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("jdoe@example.com"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output); //把它序列化到輸入輸出流中,這裏就是寫文件中了.而後呢,咱們能夠從文件中再次retrieve出來 .
fstream input("myfile", ios::in | ios::binary); //讀出文件 Person person; person.ParseFromIstream(&input); //反序列化它 cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl;
protobuf的安裝:
方法一: 若是你用的ubuntu或debian的話,能夠運行: sudo apt-get install protobuf-complier 命令直接安裝.
方法二: 能夠參考這裏,https://github.com/google/protobuf,裏面是什麼我沒有怎麼仔細看哦,若是想簡單安裝的話,能夠來這裏下載protobuf-2.5.0tar.gz, 連接爲:http://pan.baidu.com/s/1i5jsGiL,密碼:50fo,下載完之後執行下面操做:
1,執行 tar –zxf protobuf-2.5.0.tar.gz 命令,解壓文件;
2,執行 cd protobuf-2.5.0命令,進入目錄。
3,執行 ./configure --prefix=加本身想安裝到的絕對目錄, 設置安裝目錄;
4,執行:make 進行編譯
5, 執行: make check
6,執行: make install 進行安裝;
最後呢, 加入它的環境變量,能夠上系統找到它: export PATH= /home/work /protobuf/bin:$PATH
而後,你在shell裏執行: protoc –version 命令,就會顯示 libprotoc 2.5.0.
安裝成功;
首先,咱們定義一個.proto文件,下面的消息SearchRequest定義了3個字段.並以此爲例:
message SearchRequest { required string query = 1; optional int32 page_number = 2; optional int32 result_per_page = 3; }
字段的類型: 能夠爲標量形式,如 string, int32等 ,也能夠爲複合形式,如枚舉類型或其它的message類型,常見的類型以下(能看清哈):
分配的標記: 如上面所示,它們後面都有一個數字,幹什麼用的呢?它們的做用就是在以二進制形式的時候,可能經過這個分配的數字標記標識對應的字段,數字的範圍能夠從1至2次方-1. 1-15佔一個字節(包括這個數字與字段類型),16-2047佔兩個字節.另個19000-19999是保留的數字標記. 在使用標記時,儘量讓常常出現的字段表示爲1-15. 還有,咱們要小的標記爲未來使用.
字段的rules:
required: 在一個well-formed mmessage裏,必定 要在這麼一個字段.
optional: 表示能夠存在能夠不存在的字段.
repeated: 表示能夠重複的字段,重複的次數能夠爲0哦. 另外,這些重複的values的順序也會被保留下來.因爲歷史緣由,爲了讓repeated的字段能夠更好的encode,如今的新代碼都會使一個選項[packed = true],如: repeated int32 samples = 4 [packed=true];
注意:當使用required字段的時候,要特徵特別特別的當心哦,爲何呢?當咱們在一個meassage裏面定義了required字段的時候,若是咱們有時候不想寫它或着發送requied字段的時候,這時候就會出現問題,old readers會認爲這個message不完整,它們就會把拒絕或着丟掉它. 因此,google的不少工程師認爲,required的good 小於 harm, 因此他們選擇只用optional 和repeated.
咱們能夠在一個.proto文件裏面定義多個message.在.proto文件裏面,咱們用 // 來增長註釋.
保留字段:在咱們在刪除或者註釋了一個字段後,若是後來有別人使用了咱們的.proto文件,爲了防止別人再用咱們刪除或着註釋過的字段的標記或名字時出現訪問時的數據錯誤,咱們應該保留咱們使用過的字段的標記或名字.方法以下:
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; } //注意:咱們不能把標記與名字和放到一個 reserved裏.
對於optional 字段, 咱們能夠設置它的默認值.當optional字段沒有一個value的時候,就用默認的value代替.格式好比:
optional int32 result_per_page = 3 [default = 10];//就就是默認10了.另外,若是咱們沒有設置認字段的時候,它會根據類型,設置系統的默認value. 對於 string, 默認爲空字符, 對於bool類型, 默認爲 false , 對於 數字類型,默認爲0, 對於枚舉類型,默認列表裏的第一個值(因此啊,設置枚舉類型時,必定特別注意啦)
message SearchRequest { required string query = 1; optional int32 page_number = 2; optional int32 result_per_page = 3 [default = 10]; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } optional Corpus corpus = 4 [default = UNIVERSAL]; }
1. 咱們能夠在枚舉變量裏面定義一個變量的別名,方法是咱們把不一樣變量名的變量值定義成同樣的,而且設置 變量 allow_alias 變true.如:
enum EnumAllowingAlias { option allow_alias = true; //必定變忘了,要不會出錯的. UNKNOWN = 0; STARTED = 1; //下面的變量的值都爲1 RUNNING = 1; }2. 枚舉類型的值應該爲一個32位的整數, 因爲枚舉類型的值在encoding時,爲變長整數的編碼方式,對於 負數來講效率是很低的,因此不建議用負數做爲值.
3. 咱們能夠把枚舉類型定義在一個message裏面,也能夠定義在外面,這樣的話,在一個.proro文件裏,全部的message均可以使用它. 另外,也能夠把一個枚舉類型定義在其它的.proto文件裏,使用的時候的syntax爲:
MessageType.EnumType
.
咱們可使用一個message類型做爲一個字段的type.
message SearchResponse { repeated Result result = 1; } message Result { required string url = 1; optional string title = 2; repeated string snippets = 3; }
當咱們想使用別的 .proto文件裏定義的message的時候怎麼辦呢? 咱們能夠像在C/C++裏導入頭文件同樣,使用import語句,能夠把別的.proto文件導入進來. 如:
import "myproject/other_protos.proto";默認地,咱們只能使用直接導入的.proto文件,(即,若是咱們import的一個文件裏又import了其它.proto文件,可是咱們不能使用間接使用哦). 不過有一個方法, 可讓咱們作到傳遞式的引用.即, import pulic notion.
// 第一個 .proto文件的位置; //各類定義;// 第二個.proto文件 import public "第一個文件" import "其它文件"// 各類定義// 第三個.proto文件 import "第二個文件" //這是,咱們可使用第一個文件裏的定義,第二個文件裏的定義,可是不能使用其它文件裏的定義import文件時的搜索路經: 能夠經過 –I/ –proto_path指定搜索路經, 或着it looks in the directory in which the compiler was invoked.
(對於這部分如今不先看,由於用不着,要不看了也沒有用.)
當咱們有不少可選的字段的時候,而且在不少狀況下最多有一個字段被使用時,這時咱們能夠做用Oneof.如:
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }這時,在Oneof 裏的全部字段不會包含 required, optional ,或repeated 類型說明符. 它們共用一個memory,所示能夠節約內存.咱們可使用case()或WhichOneof()方法來查看哪個value被使用(使用哪個方法決定咱們使用的語言).
注意:當咱們爲Oneof裏的多個字段設置值時,只有最後一個被設置的字段被保留下來了.
注意它的後向兼容性。
Map的使用:
能夠增長一個map:
map<key_type, value_type> map_field = N;其中,key_type,能夠爲任意的整數或者string類型, value_type,能夠爲任意的類型. 以下爲一個例子,咱們把一個 Project的 Message類型與一個 string的value值相綁定了。
map<string, Project> projects = 3;
Packages:
它存在的目的是爲了防止在.proto文件裏的名字衝突. 就像C++裏的命名空間差很少.以下所示:
首先咱們定義了一個這個:
package foo.bar; message Open { ... }而後再咱們使用它的時候,咱們要這麼用:
message Foo { ... required foo.bar.Open open = 1; //看到沒,咱們要加上它的package 的名字. ... }
另外,還有不少能夠選的選項,見:https://developers.google.com/protocol-buffers/docs/proto.