protobuf 更新消息和扩展,包
一、更新一个消息类型
如果一个已有的消息格式已无法满足新的需求——如,要在消息中添加一个额外的字段——但是同时旧版本写的代码仍然可用。不用担心!更新消息而不破坏已有代码是非常简单的。在更新时只要记住以下的规则即可。
1.不要更改任何已有的字段的数值标识。
2.所添加的任何字段都必须是optional或repeated的。这就意味着任何使用“旧”的消息格式的代码序列化的消息可以被新的代码所解析,因为它们不会丢掉任何required的元素。应该为这些元素设置合理的默认值,这样新的代码就能够正确地与老代码生成的消息交互了。类似地,新的代码创建的消息也能被老的代码解析:老的二进制程序在解析的时候只是简单地将新字段忽略。然而,未知的字段是没有被抛弃的。此后,如果消息被序列化,未知的字段会随之一起被序列化——所以,如果消息传到了新代码那里,则新的字段仍然可用。注意:对Python来说,对未知字段的保留策略是无效的。
3.非required的字段可以移除——只要它们的标识号在新的消息类型中不再使用(更好的做法可能是重命名那个字段,例如在字段前添加“OBSOLETE_”前缀,那样的话,使用的.proto文件的用户将来就不会无意中重新使用了那些不该使用的标识号)。
4.一个非required的字段可以转换为一个扩展,反之亦然——只要它的类型和标识号保持不变。
5.int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来读取,那么它就会被截断为32位的数字)。
6.sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。
7.string和bytes是兼容的——只要bytes是有效的UTF-8编码。
8.嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。
9.fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。
二、扩展
通过扩展,可以将一个范围内的字段标识号声明为可被第三方扩展所用。然后,其他人就可以在他们自己的.proto文件中为该消息类型声明新的字段,而不必去编辑原始文件了。看个具体例子:
|
message Foo { // … extensions 100 to 199; } |
这个例子表明:在消息Foo中,范围[100,199]之内的字段标识号被保留为扩展用。现在,其他人就可以在他们自己的.proto文件中添加新字段到Foo里了,但是添加的字段标识号要在指定的范围内——例如:
|
extend Foo { optional int32 bar = 126; } |
这个例子表明:消息Foo现在有一个名为bar的optional int32字段。
当用户的Foo消息被编码的时候,数据的传输格式与用户在Foo里定义新字段的效果是完全一样的。
然而,要在程序代码中访问扩展字段的方法与访问普通的字段稍有不同——生成的数据访问代码为扩展准备了特殊的访问函数来访问它。例如,下面是如何在C++中设置bar的值:
|
Foo foo; foo.SetExtension(bar, 15); |
类似地,Foo类也定义了模板函数 HasExtension(),ClearExtension(),GetExtension(),MutableExtension(),以及 AddExtension()。这些函数的语义都与对应的普通字段的访问函数相符。要查看更多使用扩展的信息,请参考相应语言的代码生成指南。注:扩展可 以是任何字段类型,包括消息类型。
1.嵌套的扩展
可以在另一个类型的范围内声明扩展,如:
|
message Baz { extend Foo { optional int32 bar = 126; } … } |
在此例中,访问此扩展的C++代码如下:
|
Foo foo; foo.SetExtension(Baz::bar, 15); |
一个通常的设计模式就是:在扩展的字段类型的范围内定义该扩展——例如,下面是一个Foo的扩展(该扩展是Baz类型的),其中,扩展被定义为了Baz的一部分:
|
message Baz { extend Foo { optional Baz foo_ext = 127; } … } |
然而,并没有强制要求一个消息类型的扩展一定要定义在那个消息中。也可以这样做:
|
message Baz { … } extend Foo { optional Baz foo_baz_ext = 127; } |
事实上,这种语法格式更能防止引起混淆。正如上面所提到的,嵌套的语法通常被错误地认为有子类化的关系——尤其是对那些还不熟悉扩展的用户来说。
2.选择可扩展的标符号
在同一个消息类型中一定要确保两个用户不会扩展新增相同的标识号,否则可能会导致数据的不一致。可以通过为新项目定义一个可扩展标识号规则来防止该情况的发生。
如果标识号需要很大的数量时,可以将该可扩展标符号的范围扩大至max,其中max是2^29 - 1, 或536,870,911。如下所示:
|
message Foo { extensions 1000 to max; } |
通常情况下在选择标符号时,标识号产生的规则中应该避开[19000-19999]之间的数字,因为这些已经被Protocol Buffers实现中预留了。
三、包(Package)
当然可以为.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突。如:
|
package foo.bar; message Open { ... } |
在其他的消息格式定义中可以使用包名+消息名的方式来定义域的类型,如:
|
message Foo { ... required foo.bar.Open open = 1; ... } |
包的声明符会根据使用语言的不同影响生成的代码。对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中;对于Java,包声明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package;对于 Python,这个包声明符是被忽略的,因为Python模块是按照其在文件系统中的位置进行组织的。
1.包及名称的解析
Protocol buffer语言中类型名称的解析与C++是一致的:首先从最内部开始查找,依次向外进行,每个包会被看作是其父类包的内部类。当然对于 (foo.bar.Baz)这样以“.”分隔的意味着是从最外围开始的。ProtocolBuffer编译器会解析.proto文件中定义的所有类型名。 对于不同语言的代码生成器会知道如何来指向每个具体的类型,即使它们使用了不同的规则。
原文
http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html
protobuf 更新消息和扩展,包的更多相关文章
- 正确的 Composer 扩展包安装方法
问题说明 我们经常要往现有的项目中添加扩展包,有时候因为文档的错误引导,如下图来自 这个文档 的: composer update 这个命令在我们现在的逻辑中,可能会对项目造成巨大伤害. 因为 com ...
- 使用 Composer 安装Laravel扩展包的几种方法
使用 Composer 安装Laravel扩展包的几种方法 以下的三种方法都是需要你在项目的根目录运行 第一种:composer install 如有 composer.lock 文件,直接安装,否则 ...
- Composer 扩展包安装方法
问题说明 我们经常要往现有的项目中添加扩展包,有时候因为文档的错误引导,如下图来自 这个文档 的: composer update 这个命令在我们现在的逻辑中,可能会对项目造成巨大伤害. 因为 com ...
- 如何正确使用 Composer 安装 Laravel 扩展包
我们经常要往现有的项目中添加扩展包,有时候因为文档的错误引导,如下图来自 这个文档 的: composer update 这个命令在我们现在的逻辑中,可能会对项目造成巨大伤害. 因为 composer ...
- Visual Studio 2015的Web扩展包
过去几年,Visual Studio扩展功能生态系统得到了蓬勃发展,社区贡献出了大量优秀的扩展,其中也包括大量针对Web开发的扩展.但是很多时候,感觉寻找.安装.更新好 几个扩展,总显得比较麻烦.如果 ...
- 【转】下载量最高的 100 个 Laravel 扩展包推荐
说明 Laravel 另一个令人喜欢的地方,是拥有活跃的开发者社区,而活跃的开发者社区带来的,是繁华的扩展包生态. 本文对 Packagist 上打了 Laravel 标签 的扩展包进行整理,截止到现 ...
- laravel 安装 Laravel 扩展包
问题说明 我们经常要往现有的项目中添加扩展包,有时候因为文档的错误引导,如下图来自这个文档 的: composer update 这个命令在我们现在的逻辑中,可能会对项目造成巨大伤害. 因为 comp ...
- FreeSql 扩展包实现 Dapper 的使用习惯
简介 FreeSql.Connection.Extensions 这是 FreeSql 衍生出来的扩展包,实现(Mysql/postgresql/sqlserver/Oracle/SQLite)数据库 ...
- Python框架学习之Flask中的常用扩展包
Flask框架是一个扩展性非常强的框架,所以导致它有非常多的扩展包.这些扩展包的功能都很强大.本节主要汇总一些常用的扩展包. 一. Flask-Script pip install flask-scr ...
随机推荐
- P1417 烹调方案 背包DP
题目背景 由于你的帮助,火星只遭受了最小的损失.但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星.不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~ gw还是会做饭的,于是拿出了储藏的 ...
- 少走弯路,一个老程序猿谈PHP职业发展规划
PHP是一个使用者非常多的开发语言,但在每个领域里的开发侧重点有所不同互联网方面,在稳定的基础上,更注重性能.高并发,高负载的处理. PHP职业发展规划一般有三条路线,一条技能专精发展路线.另两条,是 ...
- CUDA安装出现图形驱动程序安装失败
win7安装cuda9时出现图形驱动程序安装失败,解决办法是右键计算机>管理>服务和应用程序>服务>找到“Windows Installer”,右键选择“启动” 参考自http ...
- Node.js包的依赖及版本号(转)
原文: http://www.cnphp6.com/archives/64130 Node.js最重要的一个文件就是package.json,其中的配置参数决定了功能.例如下面就是一个例子 { &q ...
- 使用ApiPost测试接口时需要先登录怎么办?利用Cookie模拟登陆!
ApiPost简介: ApiPost是一个支持团队协作,并可直接生成文档的API调试.管理工具.它支持模拟POST.GET.PUT等常见请求,是后台接口开发者或前端.接口测试人员不可多得的工具 . 下 ...
- Linux设备驱动之USB
Linux驱动框架分析(一) 事实上,Linux的设备驱动都遵循一个惯例——表征驱动程序(用driver更贴切一些,应该称为驱动器比较好吧)的结构体,结构体里面应该包含了驱动程序所需要的 ...
- BZOJ3779 : 重组病毒
一个点的感染时间为它到根路径上虚边数+1. 用Link-Cut Tree模拟虚实边切换,每次切换时等价于在一段或两段DFS序区间更新,线段树维护即可. 时间复杂度$O(n\log^2n)$. #inc ...
- Codeforces Round #370 (Div. 2) A. Memory and Crow 水题
A. Memory and Crow 题目连接: http://codeforces.com/contest/712/problem/A Description There are n integer ...
- 【Go命令教程】8. go test
go test 命令用于对 Go 语言编写的程序进行测试.这种测试是以 代码包 为单位的.当然,这还需要测试源码文件的帮助.关于怎样编写并写好 Go 程序测试代码,我们会在本章的第二节加以详述.在这里 ...
- 在 DELPHI 中 procedure 型变量与 method 型变量的区别
Procedure型变量: 在DELPHI中,函数.过程的地址可以赋给一个特殊类型的变量,变量可用如下方式声明: var p : procedure(num:integer); //过程 或: var ...