Protocol Buffers的应用与分析

Protocol Buffers的应用与分析 明尘

1  Protocol Buffers的介绍

Protocol Buffers是一种用于序列化结构化数据的机制,它具有灵活、高效、自动化的特点。类似于XML,但是比XML更小巧、快捷、简单。在Google 几乎所有它内部的RPC协议和文件格式都是采用PB。
PB具有以下特点:

  1. 平台无关、语言无关
  2. 高性能 比XML块20-100倍
  3. 体积小 比XML小3-10倍
  4. 使用简单
  5. 兼容性好

在这里,我做了个小实验,将一个29230KB的自定义格式的文本数据转换成PB和XML:

 PBXML
转换后的大小21011KB43202KB
解析时间(100次循环)18610ms169251ms
完成解析所写代码行数1行50行

与官方说法的差距,主要可能是因为应用场景不同,我的测试数据中字段比较长

表1:PB与XML的实验比较

可见,PB作为一种轻量级的数据协议,在时间、空间上都有一定的优势。

2  Protocol Buffers的简单应用

2.1  创建流程

2.1.1  定义一个.proto文件

新建一个文件,命名为addressbook.proto,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package tutorial;//命名空间
option java_package = "com.example.tutorial";//生成文件的包名
option java_outer_classname = "AddressBookProtos";//类名
message Person { //要描述的结构化数据
    required string name = 1;//required表示这个字段不能为空
    required int32 id = 2;//等号后面的内容为数字别名
    optional string email = 3;//optional表示可以为空
    PhoneNumber {//内部message
        required string number = 1;
        optional int32 type = 2;
    }
    repeated PhoneNumber phone = 4
}
message AddressBook {
    repeated Person person = 1;//是个集合
}

对以上内容的一点解释:

  • PB所支持的元类型数据请参考:PB元类型数据
  • 修饰符required:这个修饰符应该谨慎使用,滥用会导致后续的修改容易出现兼容性问题;
  • 修饰符optional:对于常出现的属性,为节省空间应该取1-16的别名;
  • PB是以key-value的形式来将结构化数据序列化的。它采用了将等号后的数字别名以及属性的类型用varints编码成一个数字,来作为key。

2.1.2  使用PB编译器

输入:protoc      -I=$SRC_DIR –java_out=$DST_DIR $SRC_DIR/addressbook.proto
其中    -I指定.proto文件所在目录
–java_out指定生成java文件所在的目录

2.1.3  使用PB的API来写入和读取messages

经过以上步骤,会在指定的$DST_DIR目录下生成一个AddressBookProtos.java的类。在maven中引入protobuf-java这个依赖后,利用这个类,便能序列化/反序列化数据了。
生成的代码结构如下:

1
2
3
4
5
6
7
class AddressBookProtos{
    class Person{
        class PhoneNumber{class Builder{} }
        class Builder{}
    }
    class AddressBook{class Builder{} }
}

可以看到Person、PhoneNumber、AddressBook这些内部类则对应了所定义的那些message。

2.2  序列化数据及分析

通过阅读代码可以看到,以上三个类的成员变量都是private类型的,并且,只提供了getter方法,而没有提供setter方法去为数据变量赋值。
PB利用了内部类可以访问到外部类中私有成员变量的特性。对外部类的任何赋值操作都需要通过Builder内部类来进行。Builder中有一个指向外部类的引用(名为result),当赋值完成,调用Builder的build()方法时,会把这个对象返回,同时使result指向null。
PB通过这样一种方式保证了数据安全性,一旦数据构建完毕,将无法再对其进行修改。
拿PhoneNumber这个类来说,对成员变量number、type赋值,需要以如下方式来进行:

1
2
3
4
5
6
7
PhoneNumber.Builder builder = PhoneNumber.newBuilder();
//调用setter赋值,setter返回了this,所以可以链式表述
builder.setNumber("111").setType(1);
//赋值完成后,调用Builder的build方法,将返回PhoneNumber对象
PhoneNumber phoneNumber = builder.build();

构建完成后,可以调用writeTo方法,将数据写入数据流中。

2.3  反序列化及分析

一行代码便能完成反序列化:

1
AddressBook  list = AddressBook .parseFrom(inputStream或buffer);

背后PB做了很多事情:

  1. 根据inputStream或者buffer去构造一个CodedInputStream;
  2. 然后使用生成代码中的mergeFrom方法,去解析二进制数据:
    首先调用CodedInputStream的readTag,也就是从中取得key值(int类型),然后通过swtich块来往对象中赋值(PB采用了Base 128 Varints的方式来编码这个数字,后面会介绍这种方式的)。
  3. 将数据解析完成后,会调用build()方法,将构建好的对象返回。

3  message的编码特点

PB之所以解析速度快、所占体积小,很大程度上是由它序列化的编码特点来决定的。

3.1 Base 128 Varints

PB采用了Base 128 Varints来变长编码整数:

  1. 变长编码的整数,它可能包含多个byte,对于每个byte的8位,其中后7位表示数值,最高的一位表示是否还有还有另一个byte,0表示没有,1表示有;
  2. 越前面的byte表示数值的低位,越后面的byte表示数值的高位;

例子:
300   varints  编码为:1010 1100 0000 0010
解释如下:
300的2进制编码为:0001 0010 1100
按照刚才的规则,高低位颠倒,截取最后的7为放在第一个byte,则第一byte为1010 1100(其中最高位1表示,后续还有byte);接着剩下的内容放到第二个byte,为0000 0010(其中最高位0表示,后续无byte,这个数到这里截止了)。
于是,合在一起为 1010 1100 0000 0010;

3.2 Key-Value

如前所述,PB的message是一系列的key-value对,在二进制数据中,使用varints数字(包含了别名以及属性类型信息)来作为key,进而通过由PB编译器生成的代码来构造以及解析数据。
PB将 key编码成下面的结构:
X YYYY ZZZ
其中:最高位X表示是否还有后续的byte来编码数字别名;YYYY用于编码别名,定义了多余16个属性,则需要用到额外的byte,所以出现频率高的字段应当取1-16的别名);ZZZ表示这个字段的类型,PB支持的属性的对应规则如下表:

TypeMeaningUsed For
0Varintint32, int64, uint32, uint64, sint32,sint64, bool, enum
164-bitfixed64, sfixed64, double
2Length-delimitedstring, bytes, embedded messages,packed repeated fields
3Start groupgroups (deprecated)
4End groupgroups (deprecated)
532-bitfixed32, sfixed32, floa

表2:PB 属性对应规则
例子:
required int32 a=1;  在应用中给a赋值150   ,序列化后08 96 01

  • 08代表的是key 0 0001 000, 最高位为0,表示这个key为一个byte,中间四位表示a的数字别名,最后三位表示a的属性类型;
  • 96 01代表的是value,二进制为:1001 0110 0000 0001
    → 001 0110    000 0001(去掉最高位)
    → 22              +  1*2^7 = 150

3.3 Zig-Zag

采用varints的方式编码有符号的整数,效率比较差,因为负数的最高位是1,这样就导致了情况类似于编码一个很大的数。

为了解决这个问题,Protocol Buffers定义了sint32/sint64属性,他们采用了“之字形”(ZigZag)编码的方式,将负数编码成正数,交替进行。看了下表就很好理解了:

Signed OriginalEncoded As
00
-11
12
-23
21474836474294967294
21474836484294967295

表3:Zig-Zag编码规则
利用这个方式,可以有效地节省存储空间,也能提高解析效率。

了解了以上内容,对于其他数据类型的编码,也是很好理解的,大家可以参考官方文档,这里不做详述。

4 其他

官方文档中,有提到PB提供了RPC的接口,但是没有提供具体实现。当在的.proto文件中,加入如下定义:

1
2
3
service XXX {
    rpc MMM(request) returns(response);
}

PB便会为你生成一个代表这个服务的XXX虚类,通过实现这个类中的abstract MMM方法,以及提供RpcChannel的实现,你便可以利用Protocol Buffers实现你的RPC了。

第三方的RPC实现大家可以参考ThirdPartyRPC

在这里,我利用了第三方实现protobuf-socket-rpc,写了一个小例子,有兴趣的可以看看。如下:Protocol buffer的rpc例子

5 小结

PB具有跨平台、解析速度快、序列化数据体积小、扩展性高、使用简单的特点。但是我们也可以看到,相比于XML,PB的数据,并不是自然可读的;同时它生成的代码不是纯pojo,对于代码有一定的侵入性。在你的项目中,如果对于以上缺点要求并不高,可以尝试着使用PB。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jenkins api_接触Jenkins(Hudson)API,第1部分

jenkins api哪一个-哈德森还是詹金斯? 都。 几个月前,我开始使用Hudson v1.395来从事这个小项目,在出现巨大分歧之后又回到了这个项目。 我以此为契机,看我将来选择永久搬到詹金斯时是否会遇到任何重大问题。 出现了一些麻烦-最值…

linux下面调试C、C++

(1)写好makefile文件(支持debug) objects Main.o Satellite.o TimeSystem.o SRPPara:$(objects) g -g -o SRPPara $(objects)Main.o:SRPPara.h Satellite.h TimeSystem.h g -c -g Main.cppSatellite.o:Satellite.h TimeSystem.h g -c -g Satellite.cpp…

Swing应用程序中的JavaFX 8 DatePicker

1.概述 本文显示了一个使用JavaFX 8 DatePicker控件的Java SE 8 Swing应用程序的示例。 DatePicker控件允许用户以文本形式输入日期或从日历弹出窗口中选择日期。 本示例使用其中带有FX控件的Swing JFrame 。 为了将FX内容嵌入Swing应用程序中, javafx.embed.swing…

java 中的override overload 比较

java 中的override & overload 比较 很久没看Java,忘记override和overload的区别了。百度了一下,把一个小弟的文章抄下来,备用。 方法的重写(Overiding)和重载(Overloading)是Java多态性的…

Apache Bench安装与使用

转载:ApacheBench(ab)使用详解 一、Apache Bench简介 ApacheBench 是 Apache 服务器自带的一个web压力测试工具,简称ab。ab又是一个命令行工具,对发起负载的本机要求很低,根据ab命令可以创建很多的并发访问…

[virtualenvwrapper] 命令小结

创建环境 mkvirtualenv env1mkvirtualenv env2环境创建之后,会自动进入该目录,并激活该环境。 切换环境 workon env1workon env2列出已有环境 workon退出环境 deactivate删除环境 rmvirtualenv创建project 项目将创建到PROJECT_HOME目录下,实…

轻松搭建一个Windows SVN服务器

轻松搭建一个Windows SVN服务器 前文所述SVN客户端使用的时候,用的SVN服务器通常为外部,例如Google Code的服务器,不过,做为一个程序开发人员,就算自己一个人写程序,也应该有一个SVN版本控制系统&#xff0…

cobertura覆盖率_Cobertura和Maven:集成和单元测试的代码覆盖率

cobertura覆盖率在姜黄项目中,我们每晚维护一个仪表板。 在仪表板上,我们收集有关项目的统计信息,包括代码覆盖率,findbugs分析和其他指标。 我们一直在使用Maven EMMA插件来提供代码覆盖,但是遇到了EMMA问题。 在对类…

人工智能常用 API

人工智能常用 API 转载 2016年07月13日 19:17:272047机器学习与预测 1、AlchemyAPI 在把数据由非结构化向结构化的转化中运用得较多。用于社交媒体监控、商业智能、内容推荐、金融交易和定向广告等。2、Alina一个加入了Alina的混搭网站展示了这一API在使用基因算法和人工神经…

TortoiseSVN使用帮助和下载

TortoiseSVN使用帮助和下载 随着应用软件的开发规模及复杂程度日趋大型化,使得软件开发模式从早期的个人作坊式渐渐转变为团队协作开发方式,在这种团队协作的开发模式,为了管理好开发项目,就离不开版本控制软件,在开发…

Spring框架中的内容协商

1.简介 使用BeanNameViewResolver完成的工作就是,我们刚刚在Spring上下文中创建了多个bean视图以生成预期的输出。 Spring很快引入了内容协商策略 ,该策略可以使用传统的RESTful ResponseBody方法和HTTP消息转换器,以JSON或XML返回所需的输出…

对npm的认识

npm由三个不同的组件组成:1,网站 2.命令行界面(CLI)3.注册表 需要在网站注册 命令行界面用来进行交互 注册表来进行保存 安装本地软件包 npm install 包名 更新本地软件包 npm update 包名 卸载本地软件包 npm uninstall …

Ubuntu中安装Eclipse的SVN插件——subclipse

Ubuntu中安装Eclipse的SVN插件——subclipse (2010-07-01 18:38)分类: Linux安装好Eclipse以后 进入菜单Help->Install new Software...->add...地址选择,如果安装的Eclipse版本是3.2以上 恐怕没人装旧版的了,那么使用如下地址http://s…

logging配置

settings.py # Django的日志配置项BASE_LOG_DIR os.path.join(BASE_DIR, "log")LOGGING {version: 1, # 保留字disable_existing_loggers: False, # 禁用已经存在的logger实例# 日志文件的格式formatters: {# 详细的日志格式standard: {format: [%(asctime)s][%(…

定制Spring Data JPA存储库

Spring Data是一个非常方便的库。 但是,由于该项目是一个相当新的项目,因此功能不佳。 默认情况下,Spring Data JPA将基于SimpleJpaRepository提供DAO的实现。 在最近的项目中,我开发了一个定制的存储库基类,以便可以在…

ubuntu+eclipse+svn

ubuntueclipsesvn 2010-06-23 16:02:32| 分类: 默认分类 |字号 订阅 昨天装了一天的CVS,但是始终无法启动CVSD,防火墙也没有开,不知道是什么原因,无奈之下只好转向了SVN,也当是学习一下~~&…

Log4j 2.x XSD的描述不完整

在博客文章JAXB和Log4j XML配置文件中 ,我讨论了“与使用JAXB通过Java类处理[Log4j 1.x和Log4j 2.x] XML配置文件相关的细微差别。” 在本文中,我将探讨另一个与通过Log4j 2.x XML Schema文件Log4j-config.xsd生成的JAXB对象生成Log4j 2.x配置XML相关的挑…

Protobuf学习笔记

Protobuf学习笔记 Posted by iamxhuon 2012/05/22 Leave a comment (0)Go to commentsProtocol buffers是什么? 首先了解一下Protocol Buffers(简称ProtoBuf)是什么?官网对它的定义如下: Protocol buffers are Google’s language-neutral, …

如何掌握Java内存(并保存程序)

通过AppDynamics解决应用程序问题的速度提高了10倍–以最小的开销在代码级深度监视生产应用程序。 开始免费试用! 您花了无数小时来研究Java应用程序中的错误并在需要的地方获得其性能。 在测试期间,您注意到应用程序随着时间的推移逐渐变慢&#xff0c…

程序集版本号

程序集版本号分为4段,例如1.0.4.23。 第一段为主版本号,项目一但启动则不会更改。 第二段为次版本号,在项目功能做较大调整时增加,增量为1。 第三段为修订版本号,通常在解决缺陷或者细微功能变化时增加,增量…