proto3 協議指引

WindWant發表於2021-02-14

一、protocal buffer 是什麼?

一種序列化機制。

什麼是序列化?

一種轉化為可儲存和傳輸物件的過程。

序列化的方式有很多,那麼proto有什麼特殊的呢?

它的英文介紹裡提到了neutral這個詞,中立,無關的。

language-neutral 跨語言:它可以應用於多種開發語言之間資料互動。

platform-neutral 跨平臺:它可以執行於多種系統平臺

可擴充套件

序列化過程效能優越,速度快

序列化後為二進位制資料,相對的佔用空間更小(儲存成本及傳輸成本)及一定程度的保障資料的安全性。

提供支援多語言的自動化程式碼生成工具,開發易用性

二、下面以一個簡單地示例開始:

proto3 檔案:.proto 

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

第一行宣告當前使用的proto3版本協議語法(proto編譯器預設使用proto2版本協議語法),宣告必須為檔案的第一行,此前不能有任何內容,包括註釋。

訊息使用“message”關鍵字定義,內部以“欄位型別 欄位名稱 = 欄位序號;”形式定義所要包含額屬性。

1、序號:

每一個欄位被賦予一個唯一的序號,起始為1不可重複。通常考慮到向後相容的因素,不建議修改已定義的欄位序號。

需要注意的是,序號大小會影響序列化編碼的空間佔用,例如:

序號範圍[1,15]:proto使用1個位元組儲存欄位的序號及型別,適宜定義常用欄位。

序號範圍 [16,2047]:proto使用2個位元組儲存欄位的序號及型別。

...

序號可用域[1,229 - 1],其中[19000,19999]為proto保留序號範圍(編譯使用),不可使用。另外,開發方可以約定保留序號,以供擴充套件或其它特殊使用。

2、欄位約束

singular:更直觀的可以用optional來釋義,可選欄位,0個或1個,proto3中未預設約束。

repeated:列表集合欄位型別,可以包含 >=0 個欄位元素。 

三、資料型別

proto3編碼型別對應不同開發語言資料型別:

.proto Type說明Java Type
double   double
float   float
int32

使用可變長編碼。

對於負數編碼效率較低(可以使用sint32型別儲存)

int
int64

使用可變長編碼。

對於負數編碼效率較低(可以使用sint64型別儲存)

long
uint32 使用可變長編碼。 int[1]
uint64 使用可變長編碼。 long[1]
sint32 使用可變長編碼,儲存有符號整數。尤其對負數編碼效率更高。 int
sint64

使用可變長編碼,儲存有符號整數。尤其對負數編碼效率更高。

long
fixed32 四位元組空間佔用。儲存值>228時,儲存效率高於uint32。 int[1]
fixed64

八位元組空間佔用。儲存值>256時,儲存效率高於uint64。

long[1]
sfixed32 四位元組空間佔用 int
sfixed64 八位元組空間佔用 long
bool   boolean
string UTF-8編碼或者7位ASCII文字,長度不可超過232 String
bytes 可以儲存任何二進位制資料,長度不可超過232 ByteString

 

四、預設值

singular 型別欄位在進行編解碼時,如果沒有進行賦值則賦予預設值。不同型別使用預設值如下:

型別 預設值
string 空字串
bytes 空byte陣列
bool false
數值型別 0
enums 定義的列舉第一個元素(預設必須為0)
定義的message型別 不賦值
repeated * 空列表

proto3關於預設值的操作,在我們實際的使用中不免會造成一些困擾,我們需要去區分未知結果預設值結果兩者之間的區別。例如,我們定義了bool型別欄位updated(是否已更新),預設的false所表示未更新,則會將未知是否已更新覆蓋。

對於此,通常處理的方式是引入包裝型別wrapper,使用如下:

import "google/protobuf/wrappers.proto";

 

wappers.proto檔案定義如下:

// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Wrappers for primitive (non-message) types. These types are useful
// for embedding primitives in the `google.protobuf.Any` type and for places
// where we need to distinguish between the absence of a primitive
// typed field and its default value.
//
// These wrappers have no meaningful use within repeated fields as they lack
// the ability to detect presence on individual elements.
// These wrappers have no meaningful use within a map or a oneof since
// individual entries of a map or fields of a oneof can already detect presence.

syntax = "proto3";

package google.protobuf;

option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option cc_enable_arenas = true;
option go_package = "github.com/golang/protobuf/ptypes/wrappers";
option java_package = "com.google.protobuf";
option java_outer_classname = "WrappersProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";

// Wrapper message for `double`.
//
// The JSON representation for `DoubleValue` is JSON number.
message DoubleValue {
  // The double value.
  double value = 1;
}

// Wrapper message for `float`.
//
// The JSON representation for `FloatValue` is JSON number.
message FloatValue {
  // The float value.
  float value = 1;
}

// Wrapper message for `int64`.
//
// The JSON representation for `Int64Value` is JSON string.
message Int64Value {
  // The int64 value.
  int64 value = 1;
}

// Wrapper message for `uint64`.
//
// The JSON representation for `UInt64Value` is JSON string.
message UInt64Value {
  // The uint64 value.
  uint64 value = 1;
}

// Wrapper message for `int32`.
//
// The JSON representation for `Int32Value` is JSON number.
message Int32Value {
  // The int32 value.
  int32 value = 1;
}

// Wrapper message for `uint32`.
//
// The JSON representation for `UInt32Value` is JSON number.
message UInt32Value {
  // The uint32 value.
  uint32 value = 1;
}

// Wrapper message for `bool`.
//
// The JSON representation for `BoolValue` is JSON `true` and `false`.
message BoolValue {
  // The bool value.
  bool value = 1;
}

// Wrapper message for `string`.
//
// The JSON representation for `StringValue` is JSON string.
message StringValue {
  // The string value.
  string value = 1;
}

// Wrapper message for `bytes`.
//
// The JSON representation for `BytesValue` is JSON string.
message BytesValue {
  // The bytes value.
  bytes value = 1;
}

 

五、列舉

enum 列舉物件 {

  UNKOWN = 0; //預設值機制使用(首先必須有一個列舉值為0的列舉例項,其次相容proto2中使用第一個變數為預設值的機制)

  列舉例項 = 列舉值;

  ... ...

}

六、定義更新

1、不可修改已定義的欄位序號。

2、可以刪除已定義的欄位,但是其序號不可在被使用。

3、int32, uint32, int64, uint64及bool是相互相容的,只不過轉換過程會產生值域變更

4、sint32 和 sint64 是相互相容的。

5、byte3儲存值為有效UTF-8編碼內容時與string相互相容。

七、未知欄位

未能對應解析的欄位會儲存於未知欄位中。此機制在proto3中最初拋棄,v3.5版本重新引入。

八、Map 型別

定義如下:

map<key_type, value_type> map_field = N。

key_type:任何整形或者string型別。

value_type:可以為除了Map型別外的任何型別。