Protocol Buffers介绍

写在前面——

   如果转载请注明出处,谢谢大家支持

   同步更新51CTO博客

——Forward

我的微博——龙颜硕



基本概念

Protocol Buffers(以下简称PB)是一种独立于语言、独立于开发平台、可扩展的序列化数据结构框架,它常常被用在通信、数据序列化保存等方面。

PB是一种敏捷、高效、自动化的用于对数据进行序列化的框架。同XML比较,比XML更小、更快、更简单。你一旦定义了期望的数据结构,就可以根据定义生成特定的源码,从而轻而易举地对你的数据进行读写操作,你甚至可以在不修改原来的程序源码的情况下,更新自己的之前定义的数据结构。

PB与其他协议的比较

语言:

PBJson都是跨语言的,PB支持C++JavaPython等语言;

Json支持的语言比PB更多,类似Json结构;

XML结构支持的语言也是要比PB更多。

结构:

PB发送与接受的数据属于一个完整的对象;

Json发送与接受的是一个键值对的数据结构;

XML结构从本质上讲也是一种键值对的数据结构。

数据保存:

PB是二进制方式方式保存;

Json属于文本保存;

XML保存的是文本

(说明:文本的话更容易被破解,如果要在网络传输中使用,后面两种我们就得自己去实现加密算法来保证数据在网络中的安全性了,当然,并不是说PB的二进制就完全没法破解,只是较之其他两种结构来说安全一点)。

开发和扩展成本:

PB只要维护一份proto文件就可以直接生成特定语言的类,保证了开发的高效,降低了维护成本;

Json协议一般需要发送与接受方事先定义好结构,同时因为Json协议解析的时候是大小写敏感的,使得开发和维护成本较之PB略高;

XML结构一般来说组织和解析都是需要开发人员自己去实现,开发成本和维护成本比PBJson更高;

适用范围:

PBJson一般大都使用在客户端与服务器通信模块,主要是因为数据的组织和解析较为简单;

XML结构一般是作为配置文件来使用,主要是因为方便属性的配置和修改。

通过下面的表格我们可以更清楚这三种结构之间的关系:

协议

语言

结构

数据保存

开发成本

数据大小

适用范围

PB

C++/Java/Python

对象

二进制文件

C/S通信

Json

多种

键值对

文本

较低

一般

C/S通信

XML

多种

键值对

文本

配置文件

1

PB的使用过程

1、定义自己的数据结构

PB允许我们定义自己期望的数据结构Message,它的定义是通过.proto文件来实现的。每个Message都是一个信息的逻辑记录块,这个块包括了一系列键对值(“键”相当于生成对应类文件的属性名,“值”表示这个属性的权限)

这里是作者自己写的一个.proto文件。


ad167515td4415692294f&690

1

如图1前两行分别定义了java包名和类名,在这个结构中,作者定义了一个WorkerInfoList,这就相当于一个员工的信息列表,每个员工信息定义在Work中,其中包括了员工号(id)、员工姓名(name)、员工邮箱(email)、员工地址(address)等信息。这样我们就完成了一个PB的模板(.proto文件)。

这里需要说明的是模板定义中使用的三种不同的属性optionalrequired以及repeated

Optional属性说明该属性是可选的,就是说在组织数据包时,这个数据项可以是可有可无的。

Required属性说明该属性是必须有值的,不可为空。

Repeated属性说明该属性是一个列表,我们可以把这种数据项理解为一个list来使用。

2、生成对应语言的源码;

有了模板这是第一步,接下来就要通过这个模板去生成对应语言下的类文件,google为我们提供了生成工具(protoc.exe,很多地方都可以下载到),作者是从https://developers.google.com/protocol-buffers/这里下载了(作者在使用的时候,ProtocolBuf最新版本是2.4.1)。

下载完成打开之后我们可以看到如下图2的目录结构:

ad167515td44157d27d8a&690

2

其中protoc工程编译之后就可以得到我们需要的protoc.exe工具。

cmd中启动protoc并给定参数就可以生成我们需要的CJavaPython的类文件。这里,作者简单对protoc生成对应文件的参数做一说明:

      --proto_path参数是指定我们的proto模板的目录(不要定义成这个模板文件,否则会报“Not find file”的错误);

第二个参数取决于我们想生成的文件类型,如果想生成C++的类参数名是“—cpp_out”;如果想生成Java的类参数名是“—java_out”;同理,如果想生成Python类,参数名就是“—python_out”。

接下来我们通过上面定义的模板来生成对应的类文件:

      C++

ad167515td441596abad3&690

3

      Java

ad167515td4415a437e79&690

4

      Python

ad167515td4415b158ca4&690

5

生成结果如下所示:

C++类文件:

ad167515td4415c56c182&690

6

Java类文件:

ad167515td4415d4573a0&690

7

Python类文件:

ad167515td4415e6a3185&690

8

这里需要单独说明一下的是Java类文件生成的时候会出现多级目录,这个前面已经说过,每一层的目录是我们在模板中定义的。

3、使用

有了类文件,我们就可以通过对应的API去组织和解析对应的数据了。这里简单说一下C++中的类调用(JavaPython中的使用可以自己去查看)。

ad167515td4416008de00&690

9

如图9是上面的模板中Worker类的id调用接口。id返回数据值,set_id设置数据值这些接口基本都可以通过名字知道其含义。

ad167515td44160f263d8&690

10

10中,是WorkInfoList的接口,因为这个类中包含了一个Workerrepeated数据项,前面说过我们可以把它当做一个list来使用,即在组织数据的时候通过add_workerinfo来添加一项员工数据,这个接口返回其指针,通过mutable_workerinfo(int index)接口来获取下标为index的数据指针并进行操作(设置数据等);在解析其参数的时候通过workinfo(int index)来获取下标为index的数据等等。具体的我们可以在真正使用的时候去进一步熟悉掌握。