Protocol Buffers 是一種輕便高效的結構化數據存儲格式,能夠用於結構化數據串行化,或者說序列化。它很適合作數據存儲或 RPC 數據交換格式。可用於通信協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。Protocol buffers 在序列化數據方面,它是靈活的,高效的。相比於 XML 來講,Protocol buffers 更加小巧,更加快速,更加簡單。一旦定義了要處理的數據的數據結構以後,就能夠利用 Protocol buffers 的代碼生成工具生成相關的代碼。甚至能夠在無需從新部署程序的狀況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,便可利用各類不一樣語言或從各類不一樣數據流中對你的結構化數據輕鬆讀寫。javascript
.proto
定義 ,不然沒法直接讀取數據內容以一段信息描述爲例java
<person> <name>James</name> <age>30</age> </person>
通過壓縮之後,去掉全部空格正常傳輸node
person { name: "James" age: "30" }
經過編碼之後,以二進制的方式進行數據傳輸git
命名規則: 包名.消息名.protogithub
syntax = "proto3"; // 定義使用版本 package xx; // 可選的package聲明符,用來防止不一樣的消息類型有命名衝突 import xx; // 導入其餘.proto文件 message xx // 消息命名 { 字段限制(可不寫) 字段類型 字段名=標識號(標記做用); }
注意:typescript
syntax = "proto3";
,則默認使用 proto2 進行解析。packageName.MessageName.protonpm
syntax = "proto3"; package lm; message helloworld { int32 id = 1; // ID string str = 2; // str int32 opt = 3; //optional field }
required
:必須賦值的字段optional
:無關緊要的字段repeated
:可重複字段(變長字段)因爲一些歷史緣由,基本數值類型的repeated的字段並無被儘量地高效編碼。在新的代碼中,用戶應該使用特殊選項[packed=true]來保證更高效的編碼json
.proto Type | NotesNotes | C++ Type | Java Type | Python Type[2] | Go Type | Ruby Type | C# Type | PHP Type |
---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | |
float | float | float | float | float32 | Float | float | float | |
int32 | 使用變長編碼,對於負值的效率很低,若是你的域有可能有負值,請使用sint64替代 | int32 | int | int | int32 | Fixnum 或者 Bignum(根據須要) | int | integer |
uint32 | 使用變長編碼 | uint32 | int | int/long | uint32 | Fixnum 或者 Bignum(根據須要) | uint | integer |
uint64 | 使用變長編碼 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string |
sint32 | 使用變長編碼,這些編碼在負值時比int32高效的多 | int32 | int | int | int32 | Fixnum 或者 Bignum(根據須要) | int | integer |
sint64 | 使用變長編碼,有符號的整型值。編碼時比一般的int64高效。 | int64 | long | int/long | int64 | Bignum | long | integer/string |
fixed32 | 老是4個字節,若是數值老是比老是比228大的話,這個類型會比uint32高效。 | uint32 | int | int | uint32 | Fixnum 或者 Bignum(根據須要) | uint | integer |
fixed64 | 老是8個字節,若是數值老是比老是比256大的話,這個類型會比uint64高效。 | uint64 | long | int/long | uint64 | Bignum | ulong | integer/string |
sfixed32 | 老是4個字節 | int32 | int | int | int32 | Fixnum 或者 Bignum(根據須要) | int | integer |
sfixed64 | 老是8個字節 | int64 | long | int/long | int64 | Bignum | long | integer/string |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | |
string | 一個字符串必須是UTF-8編碼或者7-bit ASCII編碼的文本。 | string | String | str/unicode | string | String (UTF-8) | string | string |
bytes | 可能包含任意順序的字節數據。 | string | ByteString | str | []byte | String (ASCII-8BIT) | ByteString | string |
在消息定義中,每一個字段都有惟一的一個數字標識符。這些標識符是用來在消息的二進制格式中識別各個字段的,一旦開始使用就不可以再改變。注:[1,15]以內的標識號在編碼的時候會佔用一個字節。[16,2047]以內的標識號則佔用2個字節。因此應該爲那些頻繁出現的消息元素保留 [1,15]以內的標識號。切記:要爲未來有可能添加的、頻繁出現的標識號預留一些標識號。後端
若是消息的字段被移除或註釋掉,可是使用者可能重複使用字段編碼,就有可能致使例如數據損壞、隱私漏洞等問題。一種避免此類問題的方法就是指明這些刪除的字段是保留的。若是有用戶使用這些字段的編號,protocol buffer編譯器會發出告警。瀏覽器
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
若是沒有指定默認值,則會使用系統默認值,對於string
默認值爲空字符串,對於bool
默認值爲false,對於數值類型
默認值爲0,對於enum
默認值爲定義中的第一個元素,對於repeated
默認值爲空。
message SearchRequest { enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; } Corpus corpus = 1; }
即參數限定只能其中一種
注意:
allow_alias
爲true,就能夠在枚舉結構中使用別名(兩個值元素值相同)可使用一個消息的定義做爲另外一個消息的字段類型。
message SearchResponse { repeated Result results = 1; } message Result { string url = 1; string title = 2; }
或者
message SearchResponse { message Result { string url = 1; string title = 2; } repeated Result results = 1; }
map<key_type, value_type> map_field = N;
key_type
能夠是除浮點指針或bytes
外的其餘基本類型,value_type
能夠是任意類型
message 採用首字母大寫開頭駝峯寫法。字段名採用下劃線分隔命名。
message SongServerRequest { required string song_name = 1; }
枚舉類型所有大寫,而且採用下劃線分隔命名。
enum Foo { FIRST_VALUE = 0; SECOND_VALUE = 1; }
每一個枚舉值用分號結束,不是逗號。
若是要使用 RPC(遠程過程調用)系統的消息類型,能夠在 .proto
文件中定義 RPC 服務接口,protocol buffer 編譯器將使用所選語言生成服務接口代碼和 stubs。
例如,若是你定義一個 RPC 服務GetGameList ,入參是 GameListReq 返回值是 GameListRes ,你能夠在你的 .proto
文件中定義它,以下所示:
service GameService { rpc GetGameList (GameListReq) returns (GameListRes); }
pb是一個跨語言,跨平臺,可擴展的序列化結構數據格式的方式.用於通信協議和數據保存等等,最初由谷歌設計.
pb是一個由typescript完成的純javascript實現,支持nodejs和瀏覽器,它容易使用,速度高效和.proto文件一塊兒工做.
// awesome.proto package awesomepackage; syntax = "proto3"; message AwesomeMessage { string awesome_field = 1; // becomes awesomeField }
protobuf.load("awesome.proto", function(err, root) { if (err) throw err; // 獲取消息類型 var AwesomeMessage = root.lookupType("awesomepackage.AwesomeMessage"); // 有效荷載(即參數) var payload = { awesomeField: "AwesomeString" }; // 若是有須要的話驗證參數,可能不完整或無效 var errMsg = AwesomeMessage.verify(payload); if (errMsg) throw Error(errMsg); // 建立新消息 var message = AwesomeMessage.create(payload); // 若是須要轉換的話就使用.fromObject // 將消息編碼成Uint8Array (browser) 或者 Buffer (node) var buffer = AwesomeMessage.encode(message).finish(); // ... do something with buffer // 將Uint8Array (browser) or Buffer (node)解碼回消息 var message = AwesomeMessage.decode(buffer); // ... do something with message // 若是應用使用長度限制的buffer, 也提供encodeDelimited 和 decodeDelimited. // 能夠將消息轉換會普通對象 var object = AwesomeMessage.toObject(message, { longs: String, enums: String, bytes: String, // see ConversionOptions }); });
實際項目使用咱們不直接解析.proto文件,而是將他們合併打包成成json文件引用.
首先全局安裝pb
npm/cnpm i -g protobufjs
使用內置的pbjs將.proto合併轉成json使用
pbjs -t json xx.proto xx.proto > bundle.json
命令行接口(CLI)可用於在文件格式之間進行轉換,並生成靜態代碼和TypeScript定義。
對於生產環境,建議把你全部的.proto文件打包單個json文件,它最小化了網絡請求的數量,並避免了任何解析器的開銷(提示:使用光庫):
Source | Library | Advantages | Tradeoffs |
---|---|---|---|
.proto | full | 易於編輯,與其餘庫的互操做性,沒有編譯步驟 | 一些解析和可能的網絡開銷 |
JSON | light | 易於編輯,沒有解析開銷,單個包(沒有網絡開銷) | protobuf.js 特有的一個編譯步驟 |
static | minimal | 在eval訪問被限制, 完整記錄,Small footprint for small protos | 很難編輯,沒有反射,有一個編譯步驟 |
1, 將.proto文件轉義成json;
2, 引入文件構建類型對象;
3, 實例化類型對象序列化成二進制數據發送給服務端;
4, 從服務端拿到二進制數據反序列化解析成類型對象轉成js對象使用;