ProtoBuf 3语法
定义ProtoBuf消息类型
要定义一个“搜索请求”的消息格式,每一个请求含有一个查询字符串、感兴趣的查询结果所在的页数,以及每一页多少条查询结果。可以采用如下的方式来定义消息类型的.proto文件
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- 文件的第一行指定了正在使用proto3语法。如果没有指定这个,编译器会使用proto2。这个指定语法行必须是文件的非空非注释的第一个行。
- SearchRequest消息格式有3个字段,在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个名字和一种类型。
指定字段类型
在上面的例子中,所有字段都是标量类型:
- 两个整型(page_number和result_per_page)
- 一个string类型(query)
当然,也可以为字段指定其他的合成类型,包括枚举(enumerations)或其他消息类型
分配标识号
在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。
- [1,15]之内的标识号在编码的时候会占用一个字节
- [16,2047]之内的标识号则占用2个字节
- 所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号
- 切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号
- 最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911
- 不可以使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留
指定字段规则
所指定的消息字段修饰符必须是如下之一:
-
singular:一个格式良好的消息应该有0个或者1个这种字段(但是不能超过1个)。
-
repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。
在proto3中,repeated的标量域默认情况下使用packed。
可以了解更多的pakced属性在Protocol Buffer 编码
添加更多消息类型
在一个.proto文件中可以定义多个消息类型。在定义多个相关的消息的时候,这一点特别有用——例如,如果想定义与SearchResponse消息类型对应的回复消息格式的话,你可以将它添加到相同的.proto文件中,如:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
添加注释
向.proto文件添加注释,可以使用C/C++/java风格的双斜杠(//) 语法格式,如:
message SearchRequest {
string query = 1;
// Which page number do we want?
int32 page_number = 2;
// Number of results to return per page.
int32 result_per_page = 3;
}
保留标识符(Reserved)
如果通过删除或者注释所有域,以后的用户可以重用标识号。当重新更新类型的时候,如果使用旧版本加载相同的.proto文件这会导致严重的问题,包括数据损坏、隐私错误等等。现在有一种确保不会发生这种情况的方法就是指定保留标识符,protocol buffer的编译器会警告未来尝试使用这些域标识符的用户。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
不要在同一行reserved声明中同时声明域名字和标识号
从.proto文件生成了什么?
当用protocol buffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。对Java来说,编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。
标量数值类型
一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型,以及与之对应的、在自动生成的访问类中定义的类型:
.proto Type | Notes | Java Type |
---|---|---|
double | 双精度浮点型 | double |
float | 单精度浮点型 | float |
int32 | 使用变长编码,对于负值的效率很低,如果域有可能有负值,请使用sint64替代 | int |
uint32 | 使用变长编码 | int |
uint64 | 使用变长编码 | long |
sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int |
sint64 | 使用变长编码,有符号的整型值。编码时比 |