protocol buffers是什么?
Protocol buffers 是google的跨语言,跨平台,可扩展的可序列化结构数据,像xml,但是更小,更快和更简单。你只需要定义一次数据结构,然后通过指定语言的生成器,就可以生成指定语言的数据结构和数据流的源码。
Protocol buffers 目前支持 java ,Python,Object-C 和 C++。当使用3.0版本的时候,还支持Go,JavaNano,Ruby和C#,以后还会支持更多语言。
Language Guide (proto3)
定义一个Message类型
先看一个简单的例子。定义一个搜索需要的数据结构格式(SearchRequest ),首先是搜索的内容query字段,然后是当前搜索的页码(page)和每页数据大小(result_per_page)。这里就是你定义的在.proto文件里的Message类型。
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- 第一行的
syntax: "proto3"
如果你不写,编译器会猜测你用的是proto2
,除了注释和空行,这句必须写在最前头。 -
SearchRequest
Message定义了三个字段(键值对),每个字段都有类型和名称。
字段类型
在上面的例子中,字段包含两个整形(page_number和result_per_page)和一个字符串(query)。其实还有枚举(enumerations)和其他类型。
分配标签
正如你所见,Message中的每个字段都有一个唯一的数字标签。这些数字标签用于区别这些字段在Message二进制流中的位置,并且当你的Message已经投入使用之后不能再修改。注意这些标签的值如果在1-15之间会占用1byte数据(包括数据类型和标签值),标签的值如果在6-2047之间将占用2byte数据。所以应该使用1-15对于经常传递的Message结构。记得留一些空间给经常传递的Message结构用于以后的添加功能。
最小的标签值是1,最大是229-1或者536,870,911。不能使用19000(FieldDescriptor::kFirstReservedNumbe)到19999(FieldDescriptor::kLastReservedNumber),它们是Protocol Buffers实现中的保留标签值。
字段规则
Message中的字段可以是下面的情况:
- singular: 一个Message的实例可以有1个或者0个对于这个字段。
- repeated: 一个Message的实例可以有0到多个,且顺序会被保留。
添加新的Message
多个Message可以被定义到同一个proto文件了。这对要定义多个相关的Message很有用。所以当你要定义一个查询结果的Message时,例如SearchResponse,可以写在同一个proto文件。
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...
}
添加注释
proto文件的注释与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.
}
保留字段
当你删除了Message中的一个字段时,别人可能会重用你的标签值。这会导致加载旧proto的服务器出问题,例如数据冲突,安全bug等等。通过reserved
可以防止这种状况的发生。当重用这些字段或者标签值的时,protocol buffer编译器会发出提示。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
注意你不能在一个reserved 中同时标记字段和标签值。
通过.proto文件会生成什么?
当编译proto文件时,编译会根据文件中标记的语言生成对应的字段get和set方法,以及Message序列化到输出流和通过输入流生成Message。
- 对于C++,编译会生成.h和.cc文件,里面包含proto文件中的每个Message对应的Class。
- 对于java,编译会生成.java文件里面包含对应每个Message的class,和用于生成Message实例的Builder类
- 对于Python
- 对于Go,编译会生成一个.pb.go文件,里面包含每个Message对于的类型。
- 对于Ruby,编译会生成一个.rb文件,里面包含每个Message对应的Ruby模块。
- 对于JavaNano,编译生成类似java,但是没有生成Builder类。
- 对于Object-C,编译会生成pbobjc.h和pbobjc.m文件,里面包含proto文件中的每个Message对应的Class。
- 对于C#,编译会生成.cs文件里面包含对应每个Message的class。
字段类型
一个Message字段可以有下面的类型,这个表格显示proto文件中的类型和对应语言的类型。
.proto Type | Notes | C++ Type | Java Type |
---|---|---|---|
double | double | double | |
float | float | float | |
int32 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 | int32 | int |
int64 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64 | int64 | long |
uint32 | 使用可变长度编码 | uint32 | int[1] |
uint64 | 使用可变长度编码 | uint64 | long[1] |
sint32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效 | int32 | int |
sint64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效 | int64 | long |
fixed32 | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 | int |
fixed64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 | long |
sfixed32 | 总是4个字节。 | int32 | int |
sfixed64 | 总是8个字节。 | int64 | long |
bool | bool | boolean | |
string | UTF-8或者7bit-acsii编码的字符串 | string | string |
bytes | 可能包含任意顺序的字节数据。 | string | ByteString |
[1] 在java中,无符号32位和64位整形使用的是对应的有符号整形。
网友评论