ProtoBuf
參考網(wǎng)址:https://developers.google.com/protocol-buffers/docs/proto3
ProtoBuf是Google的與語言無關(guān)赡鲜,與平臺無關(guān)碟狞,可擴展的機制得问,用于對結(jié)構(gòu)化數(shù)據(jù)進行序列化–以XML為例榜旦,但更小,更快弛姜,更簡單柠硕。您定義要一次構(gòu)造數(shù)據(jù)的方式,然后可以使用生成的特殊源代碼輕松地使用各種語言在各種數(shù)據(jù)流中寫入和讀取結(jié)構(gòu)化數(shù)據(jù)总棵。
Defining A Message Type
定義一個消息格式
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- 第一行:
syntax = "proto3";
如果你不指定的話鳍寂,默認使用proto2
-
SearchRequest
定義了具體的三個屬性(name/value pairs),每個字段包含名稱和類型
Specifying Field Types
屬性類型是一個(scalar types)標(biāo)量類型,你可以自定義一個枚舉類型情龄,后面我們會提到這一點
Assigning Field Numbers
每一個信息有一個唯一的數(shù)值(unique number)
這個字段被用來定義message binary format
消息二進制格式迄汛。并且一旦確定并使用了不應(yīng)該被改變。在1-15
之間使用一個字節(jié)骤视,在16-2047
使用兩個字節(jié)鞍爱。
最小的數(shù)字是1,最大的數(shù)字是2^29 - 1
专酗,同時19000-19999
是官方使用的字段睹逃,你也不可以使用
Specifying Field Rules
消息字段可以是以下兩種之一
- singular:這個字段可以有0個或1個。這是proto3默認的
- repeated:這個字段可以是0個或無線多個。
repeated
默認使用packed
編碼
Adding More Message Types
你一個在一個.proto
添加多個message
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
Adding Comments
增加注釋
使用//
或者/*...*/
/* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response. */
message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want?
int32 result_per_page = 3; // Number of results to return per page.
}
Reserved Fields
保留字段沉填,如果你更新了message疗隶,刪除了一些字段,并且將來有些人重用了這些number翼闹“弑牵可能會造成一些問題。你可以保留這些number(可能的話保留字段名)
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
What's Generated From Your .proto
?
For Go, the compiler generates a .pb.go
file with a type for each message type in your file.
Scalar Value Types 標(biāo)量值類型
Default Values(默認值)
Strings:""
Bytes:''
Bool:false
Numeric Types:0
Enums:默認值是第一個被定義的enum value猎荠,必須是0
默認的repeated是一個空列表
Enumerations(枚舉)
在下面的示例中坚弱,我們添加了一個名為Corpus的枚舉,其中包含所有可能的值以及一個Corpus類型的字段:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
- 零值必須是第一個值关摇,必須有一個零值
你可以設(shè)置別名史汗,但必須將allow_alias
設(shè)置為true
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
message MyMessage2 {
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
}
枚舉的值必須在32位證書內(nèi)
你可以設(shè)置枚舉的保留值
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
Using Other Message Types
你可以使用內(nèi)置的message作為其他message的字段
// 將Result內(nèi)置到SearchResponse
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
Importing Definitions 引入定義
你可以從其他proto
引入message
import "myproject/other_protos.proto";
默認情況下,您只能使用直接導(dǎo)入的.proto文件中的定義拒垃。但是停撞,有時您可能需要將.proto文件移動到新位置。現(xiàn)在悼瓮,您可以直接在原始位置放置一個虛擬.proto文件戈毒,而不是直接移動.proto文件并一次更改所有呼叫站點,而是使用導(dǎo)入公共概念將所有導(dǎo)入轉(zhuǎn)發(fā)到新位置横堡。導(dǎo)入包含導(dǎo)入公共聲明的原型的任何人都可以可傳遞地依賴導(dǎo)入公共依賴項埋市。例如:
// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
Nested Types 內(nèi)嵌類型
你可以使用內(nèi)嵌類型
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
你也可以復(fù)用內(nèi)嵌類型
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
你可以隨便內(nèi)嵌
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
Updating A Message Type
一些更新的規(guī)則https://developers.google.com/protocol-buffers/docs/proto3#updating
Unknown Fields
未命名類型是格式正確但解析器無法解析的字段,會被保留在序列化中
未知字段是格式正確的協(xié)議緩沖區(qū)序列化數(shù)據(jù)命贴,表示解析器無法識別的字段道宅。例如,當(dāng)舊二進制文件使用新字段解析新二進制文件發(fā)送的數(shù)據(jù)時胸蛛,這些新字段將成為舊二進制文件中的未知字段污茵。
Any
Any消息類型使您可以將消息用作嵌入類型,而無需定義它們的.proto葬项。 Any包含任意序列化消息(以字節(jié)為單位)以及URL泞当,URL作為該消息的類型并解析為該消息的類型的全局唯一標(biāo)識符。要使用Any類型民珍,您需要導(dǎo)入google / protobuf / any.proto襟士。
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
Oneof
如果你有一個message,并且包含有多個字段嚷量,并且你最多同時設(shè)置一個字段陋桂,你可以強迫使用oneof來節(jié)省內(nèi)存
它會將其他字段刪除
Using Oneof
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
Oneof Features
- 設(shè)定一個oneof將會清楚其他的oneof,如果你有幾個oneof字段蝶溶,只有最后一個才有值
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name field.
CHECK(!message.has_name());
- oneof 不能使用
repeated
- oneof也使用與反射
Backwards-compatibility issues
添加或刪除字段之一時請多加注意嗜历。如果檢查oneof的值返回None / NOT_SET,則可能意味著oneof尚未設(shè)置或已被設(shè)置為oneof的不同版本中的字段。由于無法知道導(dǎo)線上的未知字段是否是oneof的成員秸脱,因此無法分辨出兩者之間的區(qū)別落包。
Maps
如果你想創(chuàng)建一個map部蛇,可以使用以下語法
map<key_type, value_type> map_field = N;
map<string, Project> projects = 3;
Packages
你可以說增加一個可選擇的package
to.proto
避免命名沖突
package foo.bar;
message Open { ... }
你可以使用包的字段
message Foo {
...
foo.bar.Open open = 1;
...
}
Defining Services(定義服務(wù))
如果你想使用message來使用PRC摊唇,你可以定義PRC service在.proto
中。
如果你有SearchRequest
和SearchResponse
.
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
附錄:
代碼生成規(guī)則:https://developers.google.com/protocol-buffers/docs/reference/go-generated