Protocol Buffer3是什麼

Protocol Buffer

Protocol Buffers 是一種輕便高效的結構化數據存儲格式,能夠用於結構化數據串行化,或者說序列化。它很適合作數據存儲或 RPC 數據交換格式。可用於通信協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。

Protocol buffers 在序列化數據方面,它是靈活的,高效的。相比於 XML 來講,Protocol buffers 更加小巧,更加快速,更加簡單。一旦定義了要處理的數據的數據結構以後,就能夠利用 Protocol buffers 的代碼生成工具生成相關的代碼。甚至能夠在無需從新部署程序的狀況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,便可利用各類不一樣語言或從各類不一樣數據流中對你的結構化數據輕鬆讀寫。javascript

  • 數據體積小,比xml小3-5倍
  • 二進制格式,數據描述自定義,語義化清晰
  • 性能高效,比xml快20-100倍
  • 跨平臺語言,先後端通用
  • 向後兼容性好

侷限:

  • 不適合用來對基於文本的標記文檔(如 HTML)建模
  • 除非你有 .proto 定義 ,不然沒法直接讀取數據內容

以一段信息描述爲例java

xml

<person>
    <name>James</name>
    <age>30</age>
</person>

通過壓縮之後,去掉全部空格正常傳輸node

protocol buffers

person {
  name: "James"
  age: "30"
}

經過編碼之後,以二進制的方式進行數據傳輸git

示例

命名規則: 包名.消息名.protogithub

syntax = "proto3"; // 定義使用版本
package xx; // 可選的package聲明符,用來防止不一樣的消息類型有命名衝突
import xx; // 導入其餘.proto文件

message xx  // 消息命名
{ 
   字段限制(可不寫) 字段類型 字段名=標識號(標記做用);
}

注意:typescript

  • 若是開頭第一行不聲明 syntax = "proto3";,則默認使用 proto2 進行解析。
  • 語法說明(syntax)前只能是空行或者註釋
  • 每一個字段由字段限制、字段類型、字段名和編號四部分組成

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,就能夠在枚舉結構中使用別名(兩個值元素值相同)
  • 因爲枚舉值採用varint編碼,因此爲了提升效率,不建議枚舉值取負數。這些枚舉值能夠在其餘消息定義中重複使用。

嵌套類型

可使用一個消息的定義做爲另外一個消息的字段類型。

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能夠是任意類型

  • Map的字段不能夠是重複的(repeated)
  • 線性順序和map值的的迭代順序是未定義的,因此不能期待map的元素是有序的
  • maps能夠經過key來排序,數值類型的key經過比較數值進行排序
  • 線性解析或者合併的時候,若是出現重複的key值,最後一個key將被使用。從文本格式來解析map,若是出現重複key值則解析失敗。

命名規範

message 採用首字母大寫開頭駝峯寫法。字段名採用下劃線分隔命名。

message SongServerRequest {
  required string song_name = 1;
}

枚舉類型所有大寫,而且採用下劃線分隔命名。

enum Foo {
  FIRST_VALUE = 0;
  SECOND_VALUE = 1;
}

每一個枚舉值用分號結束,不是逗號

 RPC(遠程過程調用)

若是要使用 RPC(遠程過程調用)系統的消息類型,能夠在 .proto 文件中定義 RPC 服務接口,protocol buffer 編譯器將使用所選語言生成服務接口代碼和 stubs。

例如,若是你定義一個 RPC 服務GetGameList ,入參是 GameListReq 返回值是 GameListRes ,你能夠在你的 .proto 文件中定義它,以下所示:

service GameService {
  rpc GetGameList (GameListReq) returns (GameListRes);
}

ProtoBuf.js

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對象使用;

相關文章
相關標籤/搜索