文件头
1 | syntax = "proto3”; |
- syntax 指定 proto 版本类型;如果不指定,默认使用proto2;如果指定,则必须在文件的非空非注释的第一行;
- package 包管理,防止不同的消息类型产生命名冲突
- 通过import 导入其他 .proto 文件
Message类型
定义消息类型
消息类型
1
2
3
4message Param {
string key = 1; // 参数key
string value = 2; // 参数value
}嵌套消息类型
1
2
3
4
5message request {
string url = 1; // 请求路由
string method = 2; // 请求方法
repeated Param params = 3; // 请求参数列表
}
消息类型的字段可以用标量类型定义(如string,int32等),也可以用其他消息类型(如Param)来定义
字段格式
限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
1 | message request { |
1. 限量修饰符
- required: 表示是一个必须字段,在发送消息前必须设置该字段的值
- optional: 表示是一个可选字段,可设置可不设置。若发送端未设置该值,则接收端在解析时采用默认值。对于默认值,如果已设置默认值,则采用默认值,如果未设置默认值,则使用类型特定的默认值
repeated: 表示是一个重复字段,字段可包含0-N个值,类似数组的含义;属性与optional相同,可选。
注:proto3版本中取消了required/optional字段的使用,仅保留repeated。在proto3中,所有字段都是可选的。
2. 数据类型
- 标量类型:
- 其他消息类型
3. 字段名称
- 字段名称的命名与C、C++、Java等语言的变量命名方式几乎是相同的。
- protobuf建议字段的命名采用以下划线分割的驼峰式。例如 first_name 而不是firstName.
4. 字段编码值
数据在序列和反序列的时候,通信双方是如何识别对方的字段的呢?靠的是字段名么?并不是,靠的是这个字段编码值。
.proto文件:
1 | syntax = "proto3"; |
测试代码:
1 | package main |
运行结果:
1 | data: [10 9 112 97 103 101 95 115 105 122 101 18 1 49] |
可以看到尽管Param和Param1的字段名不同,但它们依旧可以互相通信。
- 为了能够相互序列及反序列化,相同的编码值,其限定修饰词和数据类型必须相同
- 编码值的取值范围为 1~2^32(4294967296)。
- 其中 1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低
- 1900~2000编码值为Google protobuf 系统内部保留值,建议不要在自己的项目中使用。
- protobuf 还建议把经常要传递的值把其字段编码设置为1-15之间的值。
- 消息中的字段的编码值无需连续,只要是合法的,但是在同一个消息类型中编码值必须唯一
5. 字段默认值
- 如果设置了默认值,如果在字段没有设置值,则使用此默认值;否则使用字段特定的默认值
- proto3中取消了字段默认值
枚举类型
1 | enum EnumAllowingAlias { |
- 枚举值必须大于等于0(proto3中第一个值必须为0)
- proto2的枚举类型不能被proto3直接import,但是间接引用不受影响。
- 通过设置可选参数allow_alias为true,就可以在枚举结构中使用别名(两个值元素值相同)
服务类型
rpc服务:
1 | service SearchService { |