编码规范:
定义一个 message 消息类型:
字段编号尽量使用 16 (虽然前15个 bytes 浪费了,但好处多多)
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
- 文件的第一行指明了我们使用的是 proto3 语法:若不指定该行 protocol buffer 编译器会认为是 proto2 。该行必须是文件的第一个非空或非注释行。
- SearchRequest 消息定义了三个字段(名称/值对),字段就是每个要包含在该类型消息中的部分数据。每个字段都具有名称和类型 。
保留字段:
在采取彻底删除或注释掉某个字段的方式来更新消息类型时,将来其他用户再更新该消息类型时可能会重用这个字段编号。后面再加载该 .ptoto 的旧版本时会引发好多问题,例如数据损坏,隐私漏洞等。一个防止该问题发生的办法是将删除字段的编号(或字段名称,字段名称会导致在 JSON 序列化时产生问题)设置为保留项 reserved。protocol buffer 编译器在用户使用这些保留字段时会发出警告。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
默认值:
当解析消息时,若消息编码中没有包含某个元素,则相应的会使用该字段的默认值。默认值依据类型而不同:
- 字符串类型,空字符串
- 字节类型,空字节
- 布尔类型,false
- 数值类型,0
- 枚举类型,第一个枚举元素
- 对于可重复类型字段的默认值是空的( 通常是相应语言的一个空列表 )。
- 内嵌消息类型,依赖于所使用的编程语言。参考 generated code guide 获取详细信息。
注意一下标量字段,在消息被解析后是不能区分字段是使用默认值(例如一个布尔型字段是否被设置为 false )赋值还是被设置为某个值的。例如你不能通过对布尔值等于 false 的判断来执行一个不希望在默认情况下执行的行为。同时还要注意若一个标量字段设置为默认的值,那么是不会被序列化以用于传输的。
在 proto3 枚举值第一个字段必须是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;
}
未知字段
未知字段是解析结构良好的 protocol buffer 已序列化数据中的未识别字段的表示方式。例如,当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。
本来,proto3 在解析消息时总是会丢弃未知字段,但在 3.5 版本中重新引入了对未知字段的保留机制以用来兼容 proto2 的行为。在 3.5 或更高版本中,未知字段在解析时会被保留同时也会包含在序列化结果中。
消息类型的更新
如果现有的消息类型不再满足您的所有需求——例如,需要扩展一个字段——同时还要继续使用已有代码,别慌! 在不破坏任何现有代码的情况下更新消息类型非常简单。仅仅遵循如下规则即可:
- 不要修改任何已有字段的字段编号
- 若是添加新字段,旧代码序列化的消息仍然可以被新代码所解析。应该牢记新元素的默认值以便于新代码与旧代码序列化的消息进行交互。类似的,新代码序列化的消息同样可以被旧代码解析:旧代码解析时会简单的略过新字段。参考未知字段获取详细信息。
- 字段可被移除,只要不再使用移除字段的字段编号即可。可能还会对字段进行重命名,或许是增加前缀 OBSOLETE_,或保留字段编号以保证后续不能重用该编号。
知识点:
field:message由一个个字段组成,一个字段的完整的二进制描述即<<编号,传输类型>,值>通常称为一个field,如下图。
- proto 是根据字段编号 (field_number) 进行序列化的而不是根据 key,即字段编号代表该字段在序列化时二进制字节流的顺序位置,所以在 grpc 的 service 端和 client 端的 proto 字段编码必须相同。
- 在对一条消息(message)进行编码的时候是把该消息中所有的key-value对序列化成二进制字节流;key和value分别采用不同的编码方式。
- 消息的二进制格式只使用消息字段的字段编号(field_num)作为Tag(key/键)的一部分,字段名和声名类型只能在解析端通过引用参考消息类型的定义(即.proto文件)才能确定。
- 解码的时候解码程序(解码器)读入二进制的字节流,解析出每一个key-value对;如果解码过程中遇到识别不出来的filed_num就直接跳过。这样的机制保证了即使该消息(message)添加了新的字段,也不会影响旧的编/解码程序正常工作。