Java中的Google协议缓冲区

总览

协议缓冲区是一种用于结构化数据的开源编码机制。 它是由Google开发的,旨在实现语言/平台中立且可扩展。 在本文中,我的目的是介绍Java平台上下文中协议缓冲区的基本用法。

Protobuff比XML更快,更简单,并且比JSON更紧凑。 当前,支持C ++,Java和Python。 但是,还有其他平台(不是Google所支持的)作为开放源代码项目–我尝试了PHP实现,但由于它尚未完全开发,因此我停止使用它。 尽管如此,支持仍在继续。 随着Google宣布支持Google App Engine中的PHP,我相信他们会将其提升到一个新的水平。

基本上,您定义使用.proto规范文件来一次构造数据的方式。 这类似于描述软件组件的IDL文件或规范语言。 协议缓冲区编译器(protoc)使用此文件,该协议缓冲区编译器将生成支持方法,以便您可以在各种流中读写对象。

消息格式非常简单。 每种消息类型都有一个或多个唯一编号的字段(稍后我们将介绍原因)。 嵌套消息类型具有其自己的唯一编号字段集。 值类型可以是数字,布尔值,字符串,字节,集合和枚举(受Java枚举启发)。 另外,您可以嵌套其他消息类型,从而使您可以按照与JSON允许的方式几乎相同的方式分层结构化数据。

字段可以指定为可选必需重复 。 在Python中实现协议缓冲区时,不要让字段的类型(例如enum,int32,float,string等)使您感到困惑。 在该领域的类型只是提示,protoc如何序列化的字段值,并产生你的邮件的邮件编码格式(以后会更多)。 编码格式看起来是对象的扁平化和压缩表示形式。 无论您是在Python,Java还是C ++中使用协议缓冲区,都将以完全相同的方式编写此规范。

Protobuff是可扩展的,您可以在以后的时间更新对象的结构,而不会破坏使用旧格式的程序。 如果要通过网络发送数据,则可以使用Protocol Buffer API对数据进行编码,然后序列化结果字符串。

可扩展性这一概念非常重要,因为Java以及与此相关的许多其他序列化机制可能会存在互操作性和向后兼容性的问题。 使用这种方法,您不必担心在代码中维护表示对象结构的serialVersionId字段。 维护该字段至关重要,因为Java的序列化机制将在反序列化对象时将其用作快速校验和。 结果,一旦将对象序列化到某个文件系统或blob存储中,以后就有可能对对象结构进行大刀阔斧的改变。 协议缓冲区受此影响较小。 只要您仅向对象添加可选字段,就可以反序列化旧类型,此时您可能会升级它们。

此外,您可以使用java_package关键字为.proto文件定义包名称。 这样可以很好地避免生成的代码发生名称冲突。 另一种选择是像在下面的示例中一样专门命名生成的类文件。 我在生成的类之前加上“ Proto”前缀,以表明这是一个生成的类。

这是一个简单的消息规范,描述了带有嵌入式地址消息User.proto的用户:

option java_outer_classname="ProtoUser";message User {required int32  id = 1;  // DB record IDrequired string name = 2;required string firstname = 3;required string lastname = 4;required string ssn= 5; // Embedded Address message specmessage Address {required int32 id = 1;required string country = 2 [default = "US"];; optional string state = 3;optional string city = 4;optional string street = 5;optional string zip = 6;enum Type {HOME = 0;WORK = 1; }optional Type addrType = 7 [default = HOME]; }repeated Address addr = 16;
}

让我们谈谈每个属性右侧看到的标签号,因为它们非常重要。 这些标记在此规范的对象上以二进制表示形式标识消息的字段顺序。 标记值1 – 15将被存储为1个字节,而标记值16 – 2047的字段则需要2个字节进行编码-不能确定为什么这样做。 Google建议您将标签1到15用于非常频繁出现的数据,并在此范围内保留一些标签值以用于将来的更新。
注意:不能使用数字19000到19999。保留用于原型实现。 另外,您可以定义必填,重复和可选的字段。从Google文档中:

  • required :格式正确的消息必须恰好具有此字段之一,即,尝试使用未初始化的必填字段来构建消息会引发RuntimeException。
  • optional :格式正确的消息可以包含零个或一个此字段(但不能超过一个)。
  • repeated :在格式正确的消息中,此字段可以重复任意次(包括零次)。 重复值的顺序将保留。

该文档警告开发人员在使用required时要谨慎因为如果您决定弃用一个字段,则这种类型的字段会引起问题。 这是所有序列化机制都会遇到的经典向后兼容性问题。 Google工程师甚至建议对所有内容使用可选。

此外,我指定了一个嵌套消息规范地址。 我可以轻松地将此定义放置在同一原型文件中的User对象之外。 因此,对于相关的消息定义,将它们全部放在同一个.proto文件中是有意义的。 即使“地址”消息类型不是一个很好的例子,但是如果消息类型在其“父”对象之外不存在,我将使用嵌套类型。 例如,如果您要序列化LinkedListNode 。 那么在这种情况下,节点将是嵌入式消息定义。 这取决于您和您的设计。

可选的消息属性被忽略时采用默认值。 特别是,使用特定于类型的默认值代替:对于字符串,默认值为空字符串;对于字符串,默认值为空字符串。 对于布尔值,默认值为false; 对于数字类型,默认值为零; 对于枚举,默认值是枚举类型定义中列出的第一个值(这很酷,但不太明显)。

枚举非常好。 它们跨平台的工作方式与Java中的enum几乎相同。 枚举字段的值可以只是一个值。 您可以在消息定义内部或外部声明枚举,就好像它是自己的独立实体一样。 如果在消息类型内指定,则可以通过[Message-name]。[enum-name]公开另一种消息类型。

协议

针对.proto文件运行协议缓冲区编译器时,编译器将生成用于所选语言的代码。 它将您的消息类型转换为增强类,其中包括为属性提供getter和setter等。 编译器还生成方便的方法,以在输出流和字符串之间来回串行化消息。

对于枚举类型,生成的代码将具有一个对应的Java或C ++枚举,或者一个特殊的Python EnumDescriptor类,该类用于在运行时生成的类中创建带有整数值的符号常量集。

对于Java,编译器将为每种消息类型生成具有流利的Design Builder类的.java文件,以简化对象的创建和初始化。 编译器生成的消息类是不可变的。 一旦建立,便无法更改。

您可以在参考资料部分中阅读有关其他平台(Python,C ++)的信息,并在此处详细介绍字段编码:

https://developers.google.com/protocol-buffers/docs/reference/overview。

对于我们的示例,我们将使用–java_out命令行标志调用protoc。 该标志向编译器指示生成的Java类的输出目录-每个原型文件一个Java类。

API

生成的API为以下便捷方法提供支持:

  • isInitialized()
  • toString()
  • mergeFrom(...)
  • 明确()

对于解析和序列化:

  • byte [] toByteArray()
  • parseFrom()
  • writeTo(OutputStream)在示例代码中用于编码
  • parseFrom(InputStream)在示例代码中用于解码

样例代码

让我们建立一个简单的项目。 我喜欢遵循Maven的默认原型:

protobuff-example / src / main / java / [应用程序代码] protobuff-example / src / main / java / gen [生成的原型类] protobuff-example / src / main / proto [原型文件定义]

为了生成协议缓冲区类,我将执行以下命令:

#  protoc --proto_path=/home/user/workspace/eclipse/trunk/protobuff/--java_out=/home/user/workspace/eclipse/trunk/protobuff/src/main/java /home/user/workspace/eclipse/trunk/protobuff/src/main/proto/User.proto

我将展示一些生成的代码,并简要介绍它们。 生成的类很大,但是很容易理解。 它将提供构建器来创建用户和地址的实例。

public final class ProtoUser {public interface UserOrBuilderextends com.google.protobuf.MessageOrBuilder...public interface AddressOrBuilderextends com.google.protobuf.MessageOrBuilder {....}

生成的类包含用于真正流畅地创建对象的Builder接口。 这些构建器接口在原型文件中指定的每个属性都有getter和setter,例如:

public String getCountry() {java.lang.Object ref = country_;if (ref instanceof String) {return (String) ref;} else {com.google.protobuf.ByteString bs =(com.google.protobuf.ByteString) ref;String s = bs.toStringUtf8();if (com.google.protobuf.Internal.isValidUtf8(bs)) {country_ = s;}return s;}}

由于这是一种自定义编码机制,因此逻辑上所有字段都具有自定义字节包装器。 我们的简单String字段在存储时使用ByteString进行压缩,然后将其反序列化为UTF-8字符串。

// required int32 id = 1;public static final int ID_FIELD_NUMBER = 1;private int id_;public boolean hasId() {return ((bitField0_ & 0x00000001) == 0x00000001);}

在这次电话会议中,我们看到了开头提到的标签号的重要性。 这些标签号似乎代表某种位位置,这些位位置定义了数据在字节串中的位置。 接下来,我们看一下前面提到的write和read方法的代码片段。

将实例写入输出流:

public void writeTo(com.google.protobuf.CodedOutputStream output)throws java.io.IOException {getSerializedSize();if (((bitField0_ & 0x00000001) == 0x00000001)) {output.writeInt32(1, id_);}if (((bitField0_ & 0x00000002) == 0x00000002)) {output.writeBytes(2, getCountryBytes());
....
}

从输入流中读取:

public static ProtoUser.User parseFrom(java.io.InputStream input)throws java.io.IOException {return newBuilder().mergeFrom(input).buildParsed();
}

此类约为2000行代码。 还有其他详细信息,例如如何映射Enum类型以及如何存储重复的类型。 希望我提供的代码片段可以使您对该类的结构有一个较高的了解。

让我们看一些使用生成的类的应用程序级代码。 要保留数据,我们可以简单地执行以下操作:

// Create instance of AddressAddress addr = ProtoUser.User.Address.newBuilder()  .setAddrType(Address.Type.HOME)        .setCity("Weston").setCountry("USA").setId(1).setState("FL").setStreet("123 Lakeshore").setZip("90210").build();// Serialize instance of UserUser user = ProtoUser.User.newBuilder() .setId(1).setFirstname("Luis").setLastname("Atencio").setName("luisat").setSsn("555-555-5555")          .addAddr(addr).build();// Write fileFileOutputStream output = new FileOutputStream("target/user.ser");  user.writeTo(output);          output.close();

一旦坚持下来,我们可以这样读:

User user = User.parseFrom(new FileInputStream("target/user.ser");System.out.println(user);

要运行示例代码,请使用:

java -cp。:../ lib / protobuf-java-2.4.1.jar app.Serialize ../target/user.ser

Protobuff与XML

Google声称协议缓冲区比XML快20到100倍(以纳秒为单位),而删除空白则小3到10倍。 但是,直到所有平台(不仅是上述3种平台)都得到支持和采用,XML仍将继续成为非常流行的序列化机制。 此外,并非每个人都具有Google用户对性能的要求和期望。 XML的替代方法是JSON。

Protobuff与JSON

我进行了一些比较测试,以评估在JSON上使用协议缓冲区的性能。 结果令人震惊,一个简单的测试显示,就存储而言,原型增益器的效率提高了50%以上。 我创建了一个简单的POJO版本的User-Address类,并使用GSON库对一个实例进行了编码,该实例的状态与上述示例相同(我将省略实现细节,请检查下面引用的gson项目)。 编码相同的用户数据,我得到:

-rw-rw-r-- 1 luisat luisat 206 May 30 09:47 json-user.ser 
-rw-rw-r-- 1 luisat luisat 85 May 30 09:42  user.ser

这很了不起。 我也在另一个博客中找到了它(请参阅下面的资源):

绝对值得一读。

结论和进一步说明

协议缓冲区可能是跨平台数据编码的良好解决方案。 使用Java,Python,C ++和其他许多语言编写的客户端,存储/发送压缩数据非常简单。

一个棘手的观点是:“永远记住需要的信息。” 如果您发疯了,并且需要.proto文件的每个字段,那么删除或编辑这些字段将非常困难。

同样有一点激励作用,即在Google的数据存储中使用probbuff 在Google的代码树中,跨12,183个.proto文件定义了48,162种不同的消息类型。

协议缓冲区促进了良好的面向对象设计,因为.proto文件基本上是愚蠢的数据持有者(如C ++中的结构)。 根据Google文档,如果您想向生成的类添加更丰富的行为,或者您无法控制.proto文件的设计,则最好的方法是将生成的协议缓冲区类包装在应用程序中,具体类别。

最后,请记住,永远不要通过从生成的类继承行为来向它们添加行为。 这将破坏内部机制,无论如何都不是一个好的面向对象的实践。

这里介绍的许多信息来自个人经验,其他资源,最重要的是来自Google开发人员代码。 请在参考资料部分中查阅文档。

资源资源

  1. https://developers.google.com/protocol-buffers/docs/overview
  2. https://developers.google.com/protocol-buffers/docs/proto
  3. https://developers.google.com/protocol-buffers/docs/reference/java-generated
  4. https://developers.google.com/protocol-buffers/docs/reference/overview
  5. http://code.google.com/p/google-gson/
  6. http://afrozahmad.hubpages.com/hub/protocolbuffers

参考:我们的JCG合作伙伴 Luis Atencio的Java协议缓冲区 ,在Reflective Thought博客上。


翻译自: https://www.javacodegeeks.com/2012/06/google-protocol-buffers-in-java.html

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

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

相关文章

匈牙利哦模板 二分匹配 完全匹配问题

匈牙利算法的核心思想就是 腾空间, 有条件 创造,没条件也要创造! bool find(int x){int i,j;for (j1;j<m;j){ //扫描每个被匹配的人 if (line[x][j]true && used[j]false) //如果有关系并且还没有标记过(这里标记的意思是这次查找曾试图改变过的归属问题&a…

ThinkPHP 中验证码的看不清切换

<!--HTML页面--> <!DOCTYPE html><html><head> <title></title></head><body><script type"text/javascript" src"__PUBLIC__/js/jquery-1.8.2.min.js"></script><form action"{:U(H…

mysql从表截取信息_mysql中循环截取用户信息并插入到目标表对应的字段中

操作环境&#xff1a;有表game_list&#xff0c;字段&#xff1a;uid&#xff0c;score1&#xff0c;score2&#xff0c;seat_id&#xff0c;last_update&#xff1b;传入参数为i_player_detail &#xff0c;传入的值为多个用户的id、之前分数、之后分数、座位号&#xff0c;每…

Java中的数组,列表,集合,映射,元组,记录文字

有时&#xff0c;当我对JavaScript的强大功能和表现力感到兴奋时&#xff0c;我发现自己错过了Java世界中的一两个功能。 除了lambda表达式/闭包或任何您想称为“匿名函数”的东西之外&#xff0c;它还对数组&#xff0c;数组&#xff0c;列表&#xff0c;集合&#xff0c;映射…

mysql锁表问题的解决方法_MYSQL锁表问题的解决方法

本文实例讲述了MYSQL锁表问题的解决方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;很多时候&#xff01;一不小心就锁表&#xff01;这里讲解决锁表终极方法&#xff01;案例一mysql>show processlist;参看sql语句一般少的话mysql>kill thread_id;就可以解…

linux——(1)初识linux

linux有窗口管理员环境和纯文本界面环境&#xff0c;同时linux默认提供6个Terminal来让用户登录。crtlaltF1-6可自由切换。其中如果窗口管理员环境处于运行状态&#xff0c;那么可以按crtlaltF7直接切过去。 常用命令&#xff1a; cd [dir] #进入dir目录下 ls #列出当前目录下的…

4.26学习成果

哇&#xff0c;今天终于开始接触Web了&#xff0c;感觉有点小兴奋&#xff0c;这几天看来那个视频感觉挺有趣的&#xff0c;挺奇妙的。看到人家敲代码&#xff0c;感觉好厉害。但是感觉不懂&#xff0c;所以&#xff0c;要努力学习了。 今天的学习成果&#xff1a; 网页由什么组…

将Glassfish 3连接到外部ActiveMQ 5代理

介绍 在ONVZ&#xff0c;我们将Glassfish 3用作开发和生产应用服务器&#xff0c;我们对其性能和稳定性以及周围的广大社区感到非常满意。 我很少遇到在stackoverflow或java.net上没有匹配解决方案的问题。 作为我们开源策略的一部分&#xff0c;我们还运行了一个定制的ActiveM…

esp8266 lcd 天气_ESP8266 显示实时天气信息

代码文件getdata.h#include #include #include #include #include #include #include #define DEBUG 1#define MAX_CONTENT_SIZE 2000const char* ssid "weather";const char* password "mymymymy";WiFiClient client;HTTPClient http;char response[MAX…

【VS开发】visual studio 2015的NuGet Manager解决方案管理功能

NuGet的官方说明是&#xff1a;NuGet是一款Visual Studio的扩展&#xff0c;它可以简单的安装、升级开源库和工具。 官网地址&#xff1a;http://www.nuget.org/ 官网最醒目的位置就是下载链接&#xff0c;安装完成后我们来快速体验一把。 手上有个小项目需要使用到json格式&am…

五. 面向对象高级特性4. 接口的概念和使用

在抽象类中&#xff0c;可以包含一个或多个抽象方法&#xff1b;但在接口(interface)中&#xff0c;所有的方法必须都是抽象的&#xff0c;不能有方法体&#xff0c;它比抽象类更加“抽象”。接口使用 interface 关键字来声明&#xff0c;可以看做是一种特殊的抽象类&#xff0…

智能配料

我们都有多少次听说“分批处理”会增加延迟&#xff1f; 作为对低延迟系统充满热情的人&#xff0c;这让我感到惊讶。 以我的经验&#xff0c;正确完成批处理不仅可以提高吞吐量&#xff0c;还可以减少平均延迟并保持一致。 那么&#xff0c;批处理如何神奇地减少延迟呢&#x…

mysql从myisam_将MySQL从MyISAM转换成InnoDB错误和解决办法

原来自己用的是为了装的&#xff0c; 所以在设置database usage(如下图1)的时候按照discuz官方的建议&#xff0c;选的都是Non-Transactional Database Only(只支持MyISAM数据引擎的非事务数据库)&#xff0c;用MyISAM数据库&#xff0c;还没涉及到需要InnoDB&#xff0c;因此打…

相似性度量中用到的一些距离函数

本文目录 1. 欧氏距离 2. 曼哈顿距离 3. 切比雪夫距离 4. 闵可夫斯基距离 5. 标准化欧氏距离 6. 马氏距离 7. 汉明距离 8. 杰卡德距离 & 杰卡德相似系数 9. 相关系数 & 相关距离 10. 信息熵 1. 欧氏距离(Euclidean Distance) 欧氏距离是最易于理解的一种距离计算方法&a…

Spring 3.1配置文件和Tomcat配置

Spring 3.1引入了非常有用的功能&#xff0c;称为配置文件 。 因此&#xff0c;它易于构建&#xff0c;可以在所有环境&#xff08;开发&#xff0c;测试&#xff0c;生产等&#xff09;中部署的软件包。 通过定义系统属性spring.profiles.active&#xff0c; Spring允许我们使…

计算1~n之间所有奇数之和_所有奇数长度子数组的和

所有奇数长度子数组的和题目&#xff1a;给你一个正整数数组 arr &#xff0c;请你计算所有可能的奇数长度子数组的和。子数组 定义为原数组中的一个连续子序列。请你返回 arr 中 所有奇数长度子数组的和 。示例 1&#xff1a;输入&#xff1a;arr [1,4,2,5,3]输出&#xff1a…

MYSQL AND OR的联用

MYSQL AND OR的联用 MYSQL中”AND”和”OR”都是条件控制符。”AND”是求交集&#xff0c;而”OR”则是求并集&#xff0c;非常多情况下&#xff0c;须要联用它们两个。下面是两张表,我仅仅列出实用的字段。 Table:student_score 学生成绩 sid(学生ID) cid(课程ID) score(分数)…

九度oj 题目1456:胜利大逃亡

题目描述&#xff1a;Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,I…

JMX:一些入门说明

JMX&#xff08;Java管理扩展&#xff09;是一种J2SE技术&#xff0c;可以管理和监视Java应用程序。 基本思想是实现一组管理对象&#xff0c;并将实现注册到平台服务器&#xff0c;在平台服务器上&#xff0c;可以使用一组连接器或适配器从本地或远程调用这些实现到JVM。 一个…

解释java程序中的异常机制_Java编程中的异常机制

本文旨在以初学者的角度来学习Java异常的知识&#xff0c;尽量简单&#xff0c;一些细枝末节的知识不会讲述&#xff0c;但不影响对知识的掌握。&#xff08;比如try-catch可以嵌套&#xff0c;不太会这么用&#xff09;1.什么是异常我们先举个例子int x 10/0;在IDE里输入这样…