介绍
Protocol Buffers
简称Protobuf
,是Google公司开发一种跨语言和平台的序列化数据结构的方式。与XML
和JSON
格式相比,protobuf
更小、更快和更便捷。你可以定义数据的结构化结构,然后使用各种语言的源代码轻松的将结构化数据写入和读取。Protocol Buffers
目前支持生成Java
,python
,obective-c
和C++
的代码,新的proto3
版本支持Dart
,Go
,Ruby
和C#
以及更多语言。
安装
去protocolbuffers/protobuf下载需要的版本,下载解压后。需要设置环境变量。假设你解压到protobuf
目录下,那么需要将protobuf/bin
目录添加到path
环境变量中。然后在命令行输入protoc --version
,如果出现版本信息,说明安装成功。
使用
添加注释
使用C/C++
风格的注释,//
和/* ... */
。
/* 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.
}
定义一个消息类型
来看一个简单的例子。假设你想要定义一个搜索请求的消息,其中每个搜索请求都有一个查询字符串以及你感兴趣的特定页面以及每页的结果。下面就是定义消息类型的.proto
文件。
syntax = "proto3"
message SearchRequest {
string query = 1;
int32 page_number= 2;
int32 result_per_page = 3;
}
文件的第一行是文件的第一个非空、非注释行,用来定义我们使用proto3
语法,如果你不这样做,protocol buffers
会认为你使用的是proto2
.
SearchRequest
消息定义三个字段,每个字段对应包含在此类消息中的每个数据。每个字段都有一个名称和类型。上面的例子中,所有字段都是标量类型。两个整数(page_number
和result_per_page
)和一个字符串(query
)。但是你可以为字段指定复合类型,包括枚举和其他消息类型。
添加多个消息类型
一个.proto
文件中可以定义多个消息类型。当你定义多个相关消息,这非常有用。例如,如果你要定义与SearchResponse
消息类型对应的回复消息格式,可以将其添加到相同的.proto
文件中。
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
指定字段数字
从上面的例子中可以看到,消息定义中每个字段都有一个唯一的编号。这些编号用于在消息二进制格式中标识字段,并且在使用消息后不应在更改。1~15
范围内的字段编号需要一个字节进行编码,包括字段编号和字段类型。16~2047
范围内的字段编号需要占用两个字节,因此,你应该为频繁出现的消息元素保留字段编号1~15
。最小的字段编号是1,最大字段编号数是2^29-1(536870911)
。但是不能使用19000~19999
编号,因为它们是protocol buffers
保留的。
使用其他消息类型
你可以使用其他消息类型作为字段类型,例如,之前的SearchResponse
消息想包含Result
消息。那么你可以在同一个.proto
文件中定义Result
消息,然后使用Result
作为SearchResponse
的字段类型。
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
上面的例子中,Result
消息类型是和SearchResponse
定义在同一个文件中。那么如果你想使用其他文件中已经定义好的字段类型,你可以通过导入语句来定义。例如import "myproject/other_protos.proto";
。
嵌套类型
你可以在消息类型里面嵌套消息类型,例如下面在SearchResponse
消息中嵌套Result
消息。
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
如果你想在非父消息中使用子消息,就需要按Parent.Type
结构来使用。
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
包
你可以添加可选的包说明添加到.proto
文件中,以防止protocol
消息类型之间的名称冲突。
package foo.bar;
message Open { ... }
然后你可以在定义消息类型的字段时,使用包说明符。
message Foo {
...
foo.bar.Open open = 1;
}
包说明符影响生成的代码的方式取决于你选择的语言。在Go中,除非在.proto
文件中明确提供选项go_package
,否则该包将用作Go包名称。
定义服务
如果你需要在RPC
系统中使用消息类型,那么你可以在.proto
文件中定义RPC
服务接口。protocol buffer
编译器会生成服务接口代码,例如,你想定义RPC服务,它有两个方法:SearchRequest
和SearchResponse
。那么你可以在.proto
文件中做如下定义:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
与protocol buffer
一起使用的最简单的RPC
系统是gRPC
,gRPC
特别适用于protocol buffer
,并允许你使用特定的protocol buffer
编译插件直接从.proto
文件生成相关的RPC
代码。
参考文章