Google Protocol Buffer(簡稱 Protobuf)是一種輕便高效的結構化數據存儲格式,平台無關、語言無關、可擴展,可用於通訊協議和數據存儲等領域。
數據交互xml、json、protobuf格式比較
1、json: 一般的web項目中,最流行的主要還是json。因為瀏覽器對於json數據支持非常好,有很多內建的函數支持。
2、xml: 在webservice中應用最為廣泛,但是相比於json,它的數據更加冗余,因為需要成對的閉合標簽。json使用了鍵值對的方式,不僅壓縮了一定的數據空間,同時也具有可讀性。
3、protobuf : 是后起之秀,是谷歌開源的一種數據格式,適合高性能,對響應速度有要求的數據傳輸場景。因為profobuf是二進制數據格式,需要編碼和解碼。數據本身不具有可讀性。因此只能反序列化之后得到真正可讀的數據。相對於其它protobuf更具有優勢
1:序列化后體積相比Json和XML很小,適合網絡傳輸
2:支持跨平台多語言
3:消息格式升級和兼容性還不錯
4:序列化反序列化速度很快,快於Json的處理速速
protoBuf的優點
Protobuf 有如 XML,不過它更小、更快、也更簡單。你可以定義自己的數據結構,然后使用代碼生成器生成的代碼來讀寫這個數據結構。你甚至可以在無需重新部署程序的情況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,即可利用各種不同語言或從各種不同數據流中對你的結構化數據輕松讀寫。它有一個非常棒的特性,即“向后”兼容性好,人們不必破壞已部署的、依靠“老”數據格式的程序就可以對數據結構進行升級。
Protobuf 語義更清晰,無需類似 XML 解析器的東西(因為 Protobuf 編譯器會將 .proto 文件編譯生成對應的數據訪問類以對 Protobuf 數據進行序列化、反序列化操作)。使用 Protobuf 無需學習復雜的文檔對象模型,
Protobuf 的編程模式比較友好,簡單易學,同時它擁有良好的文檔和示例,對於喜歡簡單事物的人們而言,Protobuf 比其他的技術更加有吸引力。
ProtoBuf 的不足
Protobuf 與 XML 相比也有不足之處。它功能簡單,無法用來表示復雜的概念。XML 已經成為多種行業標准的編寫工具,Protobuf 只是 Google 公司內部使用的工具,在通用性上還差很多。 由於文本並不適合用來描述數據結構,所以 Protobuf 也不適合用來對基於文本的標記文檔(如 HTML)建模。另外,由於 XML 具有某種程度上的自解釋性,它可以被人直接讀取編輯,在這一點上Protobuf 不行,它以二進制的方式存儲,除非你有 .proto 定義,否則你沒法直接讀出 Protobuf 的任何內容
Protobuf安裝
安裝protoBuf
#下載 protoBuf: $ git clone https://github.com/protocolbuffers/protobuf.git
#安裝依賴庫 $ sudo apt-get install autoconf automake libtool curl make g++ unzip libffidev -y
#安裝 $ cd protobuf/ $ ./autogen.sh $ ./configure $ make $ sudo make install $ sudo ldconfig # 刷新共享庫
#成功后需要使用命令測試
$ protoc –h
獲取 proto包
#Go語言的proto API接口 $ go get -v -u github.com/golang/protobuf/proto
安裝protoc-gen-go插件
#安裝 $ go get -v -u github.com/golang/protobuf/protoc-gen-go #編譯 $ cd $GOPATH/src/github.com/golang/protobuf/protoc-gen-go/ $ go build #將生成的 protoc-gen-go可執行文件,放在/bin目錄下 $ sudo cp protoc-gen-go /bin/
protobuf的語法
定義一個消息類型
syntax = "proto3"; message PandaRequest { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; }
PandaRequest消息格式有3個字段,在消息中承載的數據分別對應於每一個字段。其中每個字段都有一個名字和一種類型。
文件的第一行指定了你正在使用proto3語法:如果你沒有指定這個,編譯器會使用proto2。這個指定語法行必須是文件的非空非注釋的第一個行。
在上面的例子中,所有字段都是標量類型:兩個整型(shengao和tizhong),一個string類型(name)。
Repeated 關鍵字表示重復的那么在go語言中用切片進行代表正如上述文件格式,在消息定義中,每個字段都有唯一的一個標識符。
添加更多消息類型
在一個.proto文件中可以定義多個消息類型。在定義多個相關的消息的時候,這一點特別有用——例如,如果想定義與SearchResponse消息類型對應的回復消息格式的話,你可以將它添加到相同的.proto文件中
syntax = "proto3"; message PandaRequest { string name = 1; int32 shengao = 2; int32 tizhong = 3; } message PandaResponse { ... }
添加注釋
向.proto文件添加注釋,可以使用C/C++/java/Go風格的雙斜杠(//) 語法格式,如:
syntax = "proto3"; message PandaRequest { string name = 1; //姓名 int32 shengao = 2; //身高 int32 tizhong = 3; //體重 } message PandaResponse { ... }
從.proto文件生成了什么?
當用protocol buffer編譯器來運行.proto文件時,編譯器將生成所選擇語言的代碼,這些代碼可以操作在.proto文件中定義的消息類型,包括獲取、設置字段值,將消息序列化到一個輸出流中,以及從一個輸入流中解析消息。
對C++來說,編譯器會為每個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每一個消息有一個對應的類。
對Python來說,有點不太一樣——Python編譯器為.proto文件中的每個消息類型生成一個含有靜態描述符的模塊,,該模塊與一個元類(metaclass)在運行時(runtime)被用來創建所需的Python數據訪問類。
對go來說,編譯器會為每個消息類型生成了一個.pd.go文件。
標准數據類型
一個標量消息字段可以含有一個如下的類型——該表格展示了定義於.proto文件中的類型,以及與之對應的、在自動生成的訪問類中定義的類型:
默認值
當一個消息被解析的時候,如果被編碼的信息不包含一個特定的元素,被解析的對象鎖對應的域被設置位一個默認值,對於不同類型指定如下:
對於strings,默認是一個空string
對於bytes,默認是一個空的bytes
對於bools,默認是false
對於數值類型,默認是0
使用其他消息類型
你可以將其他消息類型用作字段類型。例如,假設在每一個PersonInfo消息中包含Person消息,此時可以在相同的.proto文件中定義一個Result消息類型,然后在PersonInfo消息中指定一個Person類型的字段
message PersonInfo { repeated Person info = 1; } message Person { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; }
使用proto2消息類型
在你的proto3消息中導入proto2的消息類型也是可以的,反之亦然,然后proto2枚舉不可以直接在proto3的標識符中使用(如果僅僅在proto2消息中使用是可以的)。
嵌套類型
你可以在其他消息類型中定義、使用消息類型,在下面的例子中,Person消息就定義在PersonInfo消息內,如:
message PersonInfo { message Person { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; } repeated Person info = 1; }
如果你想在它的父消息類型的外部重用這個消息類型,你需要以PersonInfo.Person的形式使用它,如:
message PersonMessage { PersonInfo.Person info = 1; }
當然,你也可以將消息嵌套任意多層,如:
message Grandpa { // Level 0 message Father { // Level 1 message son { // Level 2 string name = 1; int32 age = 2; } } message Uncle { // Level 1 message Son { // Level 2 string name = 1; int32 age = 2; } }
定義服務(Service)
如果想要將消息類型用在RPC(遠程方法調用)系統中,可以在.proto文件中定義一個RPC服務接口,protocol buffer 編譯器將會根據所選擇的不同語言生成服務接口代碼及存根。如,想要定義一個RPC服務並具有一個方法,該方法能夠接收 SearchRequest並返回一個SearchResponse,此時可以在.proto文件中進行如下定義:
service SearchService { //rpc 服務的函數名 (傳入參數)返回(返回參數) rpc Search (SearchRequest) returns (SearchResponse); }
Demo:
syntax = "proto2"; package proto.client_type; import "proto_basic.proto"; option java_package = "com.xxxx.common.proto"; option java_outer_classname = "ClientTypeProto"; message ClientTypeInfo { optional uint32 client_type_id = 1; // 類型ID optional string client_type_code = 2; // 類型標識 optional string client_type_name = 3; // 類型名稱 optional string create_time = 4; // 創建時間 optional string update_time = 5; // 更新時間 } //獲取終端類型列表參數 message QueryClientTypeListRequest { optional proto.basic.RequestBasic basic = 1; optional string client_type_code = 2; // 類型標識 optional string client_type_name = 3; // 類型名稱 optional uint32 index = 4; // 頁碼 optional uint32 size = 5; // 單頁長度 } //返回終端類型列表參數 message QueryClientTypeListResponse { optional proto.basic.ResponseBasic basic = 1; optional uint32 current_page = 2; // 當前頁碼 optional uint64 total_page = 3; // 總頁碼 optional uint64 total_count = 4; // 總個數 repeated ClientTypeInfo client_type_list = 5; //活動信息 }
生成proto
protoc --java_out=../java proto_upush_wx_account.proto