当你第一次定义Protocol Buffer的消息的时候,你肯定会给消息设定一套规则需求。但是随着时间的推进,你的业务可能会发生了变化,与此同时,你的Protocol Buffer消息类型的需求也会随之变化。
也就是说:有一些字段可能会发生变化,可能会添加一些字段,也可能会删除一些字段。但是可能有很多程序正在使用/读取你的Protocol Buffer的消息,但是它们没法都随着需求进行更新。所以,在你对源数据进行演进的时候,一定不要引起破坏性变化,否则其它的程序可能就无法正常工作了。
主要有这两种情景:
有时候这两种情况同时存在,也就是全兼容变更。
为了达到此目的,Protocol Buffer制定了一些更新消息类型的规则:
原来的proto是这样的:
然后我添加一个name字段:
而这时,如果把新的消息发送到旧的代码的时候,旧代码不知道2这个数字tag对应的是什么,所以name这个字段就会被忽略掉。
反过来,如果我们使用新的代码读取旧的数据,那么就会找不到新的字段,这时候就会使用该字段类型的默认值(空字符串)。
所以,处理默认值的时候一定要非常的小心。
现在我把name这个字段的名改成了full_name,而它的数字不变:
这样做是没有任何问题的。
你可以随意改变字段的名字,只要它的数字tag不变就行,因为Protocol Buffer里面这个数字tag才是最重要的。
现在我又把full_name字段删除了:
这时候,如果旧的代码找不到这个字段了,那么就会采用默认值。
反过来,如果我们使用新的代码读取旧的数据,那么已删除的字段将会被忽略/丢弃。
但是,在删除字段的时候,你应该一直都保留字段的数字tag以及字段名,像这样:
这样做是防止数字tag和名称被重复使用,避免在以后的代码库里造成冲突。
之前说了,可以把字段名改为 OBSOLETE_字段名 来代替删除字段,但是这样做的缺点就是:你还是需要把这个字段的值计算出来。我还是建议使用reserve的方式进行删除字段的管理。
注意:一定不要移除reserved的数字tags。
默认值在更新Protocol Buffer消息定义的时候有很重要的作用,它可以防止对现有代码/新代码造成破坏性影响。它们也可以保证字段永远不会有null值。
但是,默认值还是非常危险的:
那么应该怎么办?
enum同样可以进化,就和消息的字段一样,可以添加、删除值,也可以保留值。
但是如果代码不知道它接收到的值对应哪个enum值,那么enum的默认值将会被采用。
例如这个enum:
如果程序代码接收到了5这个数值,那么它找不到对应的枚举值,所以就会使用这个枚举的默认值0(UNSPECIFIED)。