reserved
标识含义及作用是什么?
在官网的文档中可以查阅到这一点:
If you update an enum type by entirely removing an enum entry, or commenting it out, future users can reuse the numeric value when making their own updates to the type. This can cause severe issues if they later load old versions of the same .proto, including data corruption, privacy bugs, and so on. One way to make sure this doesn’t happen is to specify that the numeric values (and/or names, which can also cause issues for JSON serialization) of your deleted entries are reserved. The protocol buffer compiler will complain if any future users try to use these identifiers. .1
上述主要说明了,reserved
标识两个主要用途:
- 保证
message
的向后兼容(backward-compatibility)
如果在最新版本的message
中仅仅删除了或是注释某个字段,则将来的用户将可以重新使用这个字段的编码。设计良好的协议绝不应该出现这种情况。那么为什么被删除字段的编码不能被重用,换句话说,为什么字段的编码应该保持唯一?
使用reserved
标识可以通过编译器的相关警告来提醒开发人员以避免出现这样的情况。 - 为以后升级
message
“占坑",用于保留将来字段编号。
直接在message中列出即可。
为什么字段的编码应该保持唯一?
理解这里需要从protocol buffer的编码原理出发:
在编码中,每一个字段被一个唯一的Tag标识,并且持有一个length以指定字段的长度。
当某个字段的Tag
被登记为reserved
时, 如果解析的内容存在该Tag
,程序将跳过其相应的字段。即该字段不被程序解析,从而保证了message
的后向兼容2。
-
打个不太恰当的比方:
刚开始客户端和服务端都支持处理油车、电力驱动汽车以及混合动力汽车的处理。
随着时间推移,需求发生了变化。
客户端的一方,不再支持油车设备。因而需要删除客户端的message
中的GAS
,而负责服务端的部门,还未来得及更新服务端的message
或是重启服务端需要一段时间。在这段时间中,客户端必须仍能正确处理服务端的响应消息。客户端更新前:
message Car { enum CarType { ELECTRIC = 0; GAS = 1; HYBIRD = 2; } required CarType type = 1; }
客户端更新后:
message Car { enum CarType { reserved 1; ELECTRIC = 0; //GAS = 1; 不再支持 HYBIRD = 2; } required CarType type = 1; }
即使客户端删除掉了该标识,由于服务端并没有更新,发送的信息仍然会包含该字段。如果设置
reserved
标识, 程序解析时根据所给protocol buffer的原理,会跳过这条信息,程序依旧正常工作。
使用optional
标识时必须额外关注默认值default
的影响
在未设置default
标识该optional
的字段时,对于基本类型会赋予较符合直觉的默认值。如字符串类型将会赋值为空串。但是使用枚举类型时,情况就不一样了,它将会将第一个枚举常量作为默认值。1
后记:需要更深入的了解
对于Protocol Buffer的使用,其难点主要在于兼容性上的问题。
详细可见Protocol Buffers Guide文档中的Updating A Message Type
一节及文章Protocol Buffers Best Practices for Backward and Forward Compatibility