本文将描述如何使用protocol buffer 语言构造你的protocol buffer数据,包括.proto文件语法规则,以及如何由.proto文件生成数据访问类。本文涵盖了proto3版本的协议语言:想了解proto2的语法,查看 Proto2 语言指南.
这是一个参考指南——给出了一步一步的操作的示例,示例使用了本文中描述的很多特性,这里有适合你所选语言的教程:
首先让我们来看一个非常简单的例子。假定我们有这样的需求,我们要定义一个搜索请求消息,每个消息都包含一个查询字符串,和你感兴趣的特定页面编号,以及每个页面的命中个数。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
在上述例子中,所有的字段都是值类型:两个整形(page_number and result_per_page) 和一个字符串 (query)。然而,你也可以指定你的字段的组合类型,包括枚举和其他消息类型。
如你所见,消息中的每个字段都有一个唯一的数字标识。这些标识用来在消息的二进制格式中识别你的字段,并且,一旦你的消息投入使用,这些标识就不应该再被修改。
注意,标识是由1到15使用一个字节来编码,包括标识数字和字段类型(你可以在Protocol Buffer 编码中查看更多详细)。
标识16到2047占用两个字节。所以你应该保留1到15,用作出现最频繁的消息类型的标识。记得为将来会继续增加并可能频繁出现的元素留一点儿标识区间,也就是说,不要一下子把1—15全部用完,为将来留一点儿哦。
你可以指定的最小的标识数字是1,最大是228,或者536,870,911。你也不能使用19000 到 19999之间的数字(FieldDescriptor::kFirstReservedNumber through FieldDescriptor::kLastReservedNumber),因为它们被Protocol Buffers保留使用——如果你在自己的.proto文件中使用了一个保留数字,protocol buffer 编译器将会提示。同样的,你不能使用任何之前保留的标识。
消息字段可以是下边中的一种:
singular(单个): 符合语法规则的消息包含零个或者一个这样的字段(最多一个)。
repeated(重复): 一个字段在合法的消息中可以重复出现一定次数(包括零次)。重复出现的值的次序将被保留。在proto3中,重复出现的值类型字段默认采用压缩编码。你可以在这里找到更多关于压缩编码的东西: Protocol Buffer Encoding。
多个消息类型可以定义在一个.proto文件中。这个在你定义多个关联的消息的时候非常有用,——这样,举个例子吧,如果你想定义你的搜索消息类型的响应消息格式,你可以在同一个.proto文件中添加如下的内容:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
在你的 .proto 文件中添加注释, 使用C/C++-风格的 // 语法,像下边这样:
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的旧版,包括数据损坏,安全隐私bug等等。一个确保这种问题不会发生的办法是,保留你要删除的字段的标识。Protocol buffer 编译器将会提示以后用户使用这些保留的字段标识。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
注意,你不要混淆同一个保留语句中的字段名称和标识。
当你运行对一个.proto文件的编译之后,编译器会为你选择的语言生成代码。你在文件中描述的消息类型,包括获取和设置字段的值,序列化你的消息到一个输出流,以及从一个输入流中转换出你的消息。
值类型的消息字段可以是一下类型中的一种——这个表格展示了可以在.proto文件中使用的类型,以及自动生成的相应语言的类型:
.proto Type | 说明 | C++ Type | Java Type | Python Type[2] | Go Type |
---|---|---|---|---|---|
double | double | double | float | float64 | |
float | float | float | float | float32 | |
int32 | 使用可变长度编码。对负数进行编码时比较低效 – 如果你的字段要使用负数值,请使用sint32来代替。 | int32 | int | int | int |
int64 | 使用可变长度编码。对负数进行编码时比较低效 – 如果你的字段要使用负数值,请使用sint64来代替。 | int64 | long | int/long[3] | int64 |
uint32 | 使用可变长度编码 | uint32 | int[1] | int/long[3] | uint32 |
uint64 | 使用可变长度编码 | uint64 | long[1] | int/long[3] | uint64 |