官网地址
protocol 3 语法
定义消息类型
定义查询请求消息结构,query为查询条件,page_number为感兴趣的页面,result_per_page为感兴趣的页面结果数量。内容写入.proto文件中。
//定义proto语法版本,默认为proto2
syntax = "proto3";
//定义消息结构
message SearchRequest {
//string 查询条件 字段序号为1
string query = 1;
//int 感兴趣的页面 字段序号为2
int32 page_number = 2;
//int 结果数量 字段序号为3
int32 result_per_page = 3;
}
分配字段序号
消息结构所有的字段,都有唯一的字段序号。在消息体中,通过二进制格式,识别字段序号。字段序号1到15,用一个字节标识字段序号和字段类型;16到2047,用两个字节标识字段序号和字段类型。在设计上,应该为频繁出现字段设计为1到15,为了后期扩展在设计上字段序号最好预留一些空间。 字段序号最小为1,最大为536,870,911(2的29次方减1)。19000 到 19999为预留字段,也不可以使用。
字段规则
字段规则包含单数、重复数,单数为默认字段规则。
singular 单数
repeated 重复数
增加更加消息类型
一个.proto文件中,可以定义多个消息体。
//定义proto语法版本,默认为proto2
syntax = "proto3";
//定义消息结构
message SearchRequest {
//string 查询条件 字段序号为1
string query = 1;
//int 感兴趣的页面 字段序号为2
int32 page_number = 2;
//int 结果数量 字段序号为3
int32 result_per_page = 3;
}
message SearchResponse{
}
文件注释
.proto文件注释和java代码注释一致。
/* 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字段进行了删除、注释,后期又做了一些更改,可能导致序列化失败。一种方法是通过制定占用某些字段序号、字段名称,使后期再次修改时,不可占用这些预定字段,否则编译失败。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
标量字段类型
标量字段类型,如下所示。
| .proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
|---|---|---|---|---|---|---|---|---|---|
| double | double | double | float | float64 | Float | double | float | double | |
| float | float | float | float | float32 | Float | float | float | double | |
| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
| uint32 | Uses variable-length encoding. | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
| uint64 | Uses variable-length encoding. | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
| sfixed32 | Always four bytes. | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
| sfixed64 | Always eight bytes. | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
| bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
| string | A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. | string | String | str/unicode[5] | string | String (UTF-8) | string | string | String |
| bytes | May contain any arbitrary sequence of bytes no longer than 232. | string | ByteString | str (Python 2) bytes (Python 3) | []byte | String (ASCII-8BIT) | ByteString | string | List |
字段默认值
不同字段类型有不同的默认值。一旦消息解析,不保证字段一定明确序列化为默认值,也有可能完全没有序列化,这个需要看具体语言实现方式。
- string 默认为空字符串
- bytes 默认为空bytes
- bools 默认为false
- numeric 默认为0
- enums 默认为第一个定一个的枚举,值必须为0
- 消息体字段,则看具体语言默认值
- 重复字段 默认值为空,一般为空的集合
枚举
定义枚举,第一个值为默认值,并且必须为0。
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;
}
定义枚举,可以定义别名,启用别名,则不同的值可以映射不同的枚举。
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.
}
}
使用其他类型作为字段
消息体可以用其他消息体作为字段。其他消息体可以定义在同一个文件中,也可以定义不同的文件中。定义在不同的文件中,通过**import"myproject/other_protos.proto";**引入其他文件。
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
通过引用则可以使用其他文件的消息结构。但是import作用范围只在当前文件中,如果当前文件又被import,则另一个文件用不了当前文件的引用。需要import作用范围可以传递下去,则需要用import public。
// 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
嵌套消息类型定义
消息定义内部可以再次定义消息。
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
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;
}
}
}
更新消息
如果消息类型不在符合你的要求,例如你需要额外的字段,或者删除某个字段,则需要修改协议文件。为了达到安全修改协议,需要遵守以下几个标准
- 不要修改已存在的字段序号
- 新增字段,老的协议序列化二进制可以被新的协议解析。老协议解析信的协议序列化二进制,会忽略新增字段。
- 删除字段,新的协议不要再使用删除字段的字段序号
int32,uint32,int64,uint64, 和bool相互兼容sint32和sint64相互兼容string和bytes,utf8格式相互兼容- 单数字段转成oneof可以兼容,重复数转成oneof在确保只有序列化一个时可以兼容
不认识的字段
不认识的字段为协议解析时不认识的字段。
oneof字段
如果消息有多个字段,但是只有存在其中一个,则可以定义为oneOf。
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
map类型
如果需要定义key-value类型,可以用map。key_type不可以为枚举类型。map类型不可以为重复数。
map<key_type, value_type> map_field = N;
以下写法与map效果一致。
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
package
多个消息文件,消息名一致,可以通过设置package区分。java如果没有指定java_package,则默认为package
package foo.bar;
message Open { ... }
message Foo {
...
foo.bar.Open open = 1;
...
}
其他optional属性
- java_package 包路径。
option java_package = "com.example.foo";
-
java_outer_classname
-
java_multiple_files
option java_multiple_files = true;//消息类是否生成多个文件
生成代码
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto