Protobuf在IPC中的应用

本文描述了设计IPC时需要考虑的内容,比较了Protobuf,XML,JSON之间的优缺点,得出用Protobuf序列化IPC中数据的方案,最后详细描述了怎么用Protobuf来进行序列化。

关键词:Protobuf,IPC,XML, JSON,序列化

引言

IPC(Inter-Process Communication)在功能稍微复杂一点的软件开发中几乎是必须会涉及到的内容,在设计IPC时往往需要考虑这些内容。

1. 由于IPC中所携带的数据需要进行序列化/反序列化,序列化/反序列化过程复杂程度和速度需要考虑;

2. 软件开发过程中几乎不可避免的会修改一些IPC消息,但是由于之前已经发布了版本给客户,那么IPC中携带的数据向后兼容性就相当重要;

3. 在一些资源有限的系统中(例如嵌入式系统), IPC中携带的数据大小也是需要被关注;

4. 跨平台IPC时,需要考虑平台差异性,语言差异性对数据序列化/反序列化带来的影响。

常见的序列化/反序列化手段有XML, JSON, Protobuf,其中XML和JSON序列化后都是文本,可读性很好,Protobuf序列化后是二进制数,可读性不好。Protobuf对比XML和JSON来说优点是:使用简单,可通过工具自动生成解析的类或数据结构;数据的向后兼容性很好;序列化和反序列化速度异常的快;序列化后数据量小。

基于上述的描述,用Protobuf来序列化/反序列化IPC中数据是个较优的选择。

Protobuf简介

Google Protocol Buffer( 简称Protobuf)Google旗下的一款平台无关,语言无关,可扩展的序列化结构数据格式。只要实现相同的协议格式(即同一.proto文件),就可以被编译成不同的语言版本,加入到各自的工程中去。这样不同语言就可以解析其它语言通过Protobuf序列化的数据。开发人员通过按照一定语法定义结构化数据(也称为Message),然后通过解析工具自动生成相关的类,目前可以支持JAVA, C++, python等语言环境。通过将这些类包含在项目中,可以很轻松的调用相关方法来完成业务消息的序列化和反序列化工作。

编写.proto文件中的Message时用到的关键词有:required(表示这个字段必须的,必须在序列化的时候被赋值);optional(代表这个字段是可选的,可以为0个或1个但不能大于1个);repeated(则代表此字段可以被重复任意多次包括0次)。这样每个Message可扩展性和数据的向后兼容性较好。

Protobuf序列化后所生成的二进制消息非常紧凑,这得益Protobuf 采用的非常巧妙的编码方法,它采用Varint这样一种紧凑的表示数字方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数这能减少用来表示数字的字节数。Varint中的每个字节的最高位bit有特殊的含义,如果该位为1,表示后续的字节也是该数字的一部分,如果该位为0,则结束。其他的7bit 都用来表示数字。因此小于 128 的数字都可以用一个字节表示。大于128 的数字,会用两个字节来表示1演示了Protobuf 如何解析两个字节

 

1: Varint编码方式

 

消息经过序列化后会成为一个二进制数据流,该流中的数据为一系列的Key-Value对,如图2所示。采用这种Key-Pair结构无需使用分隔符来分割不同的Field。对于可选的Field,如果消息中不存在该 Field,那么在最终Message Buffer中就没有该Field,这些特性都有助于节约消息本身的大小。

2: Message Buffer

IPC中使用Protobuf

编写.proto文件

为了序列化和反序列化数据,必须保证通讯双方使用的是同一份.proto文件。在这个文件中定义程序中需要处理的结构化数据(Message)。例如图3所示的一个简单的Message,其中第一个成员result是个枚举类型,第二个成员为protocol_version是可选的。

3:简单的Message

当需要定义复杂的Message,可以在Message中嵌套一个或者多个Message,如图4所示。

4:嵌套Message

在一个.proto 文件中,还可以用关键词import引入其他.proto文件中所定义的消息,这就可以定义更加复杂的数据结构。

5:引入其他.proto文件

编译.proto文件

写好.proto文件后就可以用Protobuf的编译器将该文件编译成目标语言了。这里以C语言为例(虽然Google目前没有提供C语言的API,但是我们可以使用开源项目Protobuf-c来作为C语言的API),使用如下命令可以在.proto文件目录中生成代码文件。

protoc --nanopb_out= . gpb_app.proto,该命令执行后,会生成gpb_app.pb.cgpb_app.pb.h这两个文件。同时还需要将Protobuf-c提供的API文件拷贝到工程中,这些API文件为:

pb.h; pb_common.c; pb_common.hpb_decode.c; pb_decode.h; pb_encode.c; pb_encode.h

 

使用序列化API

以图3所示的.proto文件为例,编译后gpb_app.pb.h文件中会包含图6所示的数据结构,

 

6:生成的数据结构

同时gpb_app.pb.c中会包含图7所示的数据结构。

7:生成的格式相关数据结构

序列化/反序列化时需要调用Protobuf-c提供的API,图8为序列化。

8:序列化

反序列化时和上面过程刚好相反,图9为反序列化。

9:反序列化

结论

综上所述,用Protobuf来序列化/反序列化IPC中的数据是一个比较好的方案,尽管序列化后数据是二进制,可读性不强,但是可以借助第三方插件来显示。Protobuf确实适合在不同应用程序和不同平台之间交换数据,本文提出的用Protobuf来序列化/反序列化IPC中的数据有一定的新颖性。IPC双方只依赖于共同的.proto文件,与平台和语言无关,这确实带来了一定的便利性和在一定程度上减少了人力沟通成本。除此之外在实际项目中.proto文件如果由架构师专门负责编写和维护,可以减少设计和实现不一致的可能性。

posted @ 2017-10-14 20:35  yuzhenjin  阅读(302)  评论(0编辑  收藏  举报