网站首页 > java教程 正文
介绍
ProtoBuf 是google团队开发的用于高效存储和读取结构化数据的工具。什么是结构化数据呢,正如字面上表达的,就是带有一定结构的数据。比如电话簿上有很多记录数据,每条记录包含姓名、ID、邮件、电话等,这种结构重复出现。
同类
XML、JSON 也可以用来存储此类结构化数据,但是使用ProtoBuf表示的数据能更加高效,并且将数据压缩得更小。
原理
Protobuf是一个网络通信协议,提供了高效率的序列化和反序列化机制,序列化就是把对象转换成二进制数据发送给服务端,反序列化就是将收到的二进制数据转换成对应的对象。(关于proto结构体怎么编写,可自行查阅文档)
举个例子
1. 先创建一个proto文件
message.proto
2. 创建一个Java项目
并且将proto文件放置 src/main/proto 文件夹下
3. 编译proto文件至Java版本
- 用命令行 cd 到 src/main 目录下
- 终端执行命令 : protoc --java_out=./java ./proto/*.proto
- 会发现,在你的src/main/java 里已经生成里对应的Java类
4. 依赖Java版本的ProtoBuf支持库
这里只举一个用Gradle使用依赖的栗子
implementation 'com.google.protobuf:protobuf-java:3.9.1'
5. 将Java对象转为ProtoBuf数据
Message.Person.Phone.Builder phoneBuilder = Message.Person.Phone.newBuilder(); Message.Person.Phone phone1 = phoneBuilder .setNumber("100860") .setType(Message.Person.PhoneType.HOME) .build(); Message.Person.Phone phone2 = phoneBuilder .setNumber("100100") .setType(Message.Person.PhoneType.MOBILE) .build(); Message.Person.Builder personBuilder = Message.Person.newBuilder(); personBuilder.setId(1994); personBuilder.setName("XIAOLEI"); personBuilder.addPhone(phone1); personBuilder.addPhone(phone2); Message.Person person = personBuilder.build(); long old = System.currentTimeMillis(); byte[] buff = person.toByteArray(); System.out.println("ProtoBuf 编码耗时:" + (System.currentTimeMillis() - old)); System.out.println(Arrays.toString(buff)); System.out.println("ProtoBuf 数据长度:" + buff.length);
6. 将ProtoBuf数据,转换回Java对象
System.out.println("-开始解码-"); old = System.currentTimeMillis(); Message.Person personOut = Message.Person.parseFrom(buff); System.out.println("ProtoBuf 解码耗时:" + (System.currentTimeMillis() - old)); System.out.printf("Id:%d, Name:%s\n", personOut.getId(), personOut.getName()); List<Message.Person.Phone> phoneList = personOut.getPhoneList(); for (Message.Person.Phone phone : phoneList) { System.out.printf("手机号:%s (%s)\n", phone.getNumber(), phone.getType()); }
比较
为了能体现ProtoBuf的优势,我写了同样结构体的Java类,并且将Java对象转换成JSON数据,来与ProtoBuf进行比较。JSON编译库使用Google提供的GSON库,JSON的部分代码就不贴出来了,直接展示结果
比较结果结果
- 运行 1 次
【 JSON 开始编码 】 JSON 编码1次,耗时:22ms JSON 数据长度:106 -开始解码- JSON 解码1次,耗时:1ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码1次,耗时:32ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码1次,耗时:3ms
- 运行 10 次
【 JSON 开始编码 】 JSON 编码10次,耗时:22ms JSON 数据长度:106 -开始解码- JSON 解码10次,耗时:4ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码10次,耗时:29ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码10次,耗时:3ms
- 运行 100 次
【 JSON 开始编码 】 JSON 编码100次,耗时:32ms JSON 数据长度:106 -开始解码- JSON 解码100次,耗时:8ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码100次,耗时:31ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码100次,耗时:4ms
- 运行 1000 次
【 JSON 开始编码 】 JSON 编码1000次,耗时:39ms JSON 数据长度:106 -开始解码- JSON 解码1000次,耗时:21ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码1000次,耗时:37ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码1000次,耗时:8ms
- 运行 1万 次
【 JSON 开始编码 】 JSON 编码10000次,耗时:126ms JSON 数据长度:106 -开始解码- JSON 解码10000次,耗时:93ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码10000次,耗时:49ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码10000次,耗时:23ms
- 运行 10万 次
【 JSON 开始编码 】 JSON 编码100000次,耗时:248ms JSON 数据长度:106 -开始解码- JSON 解码100000次,耗时:180ms 【 ProtoBuf 开始编码 】 ProtoBuf 编码100000次,耗时:51ms ProtoBuf 数据长度:34 -开始解码- ProtoBuf 解码100000次,耗时:58ms
总结
编解码性能
上述栗子只是简单的采样,实际上据我的实验发现
- 次数在1千以下,ProtoBuf 的编码与解码性能,都与JSON不相上下,甚至还有比JSON差的趋势。
- 次数在2千以上,ProtoBuf的编码解码性能,都比JSON高出很多。
- 次数在10万以上,ProtoBuf的编解码性能就很明显了,远远高出JSON的性能。
内存占用
ProtoBuf的内存34,而JSON到达106 ,ProtoBuf的内存占用只有JSON的1/3.
结尾
其实这次实验有很多可待优化的地方,就算是这种粗略的测试,也能看出来ProtoBuf的优势。
兼容
新增字段
- 在proto文件中新增 nickname 字段
- 生成Java文件
- 用老proto字节数组数据,转换成对象
Id:1994, Name:XIAOLEI 手机号:100860 (HOME) 手机号:100100 (MOBILE) getNickname=
结果,是可以转换成功。
删除字段
- 在proto文件中删除 name 字段
- 生成Java文件
- 用老proto字节数组数据,转换成对象
Id:1994, Name:null 手机号:100860 (HOME) 手机号:100100 (MOBILE)
结果,是可以转换成功。
猜你喜欢
- 2024-10-10 简单的对象转换方法类分享(对象转化为字符串的几种方式)
- 2024-10-10 为什么 JSON 不适合作为配置语言?
- 2024-10-10 JSON 对象的这些操作和使用场景你知道多少?
- 2024-10-10 第27天|Java入门有野,json操作(java的json解析几种方法)
- 2024-10-10 如何使用springmvc返回json格式的数据?
- 2024-10-10 springboot从小白到大神-007处理Json数据进阶
- 2024-10-10 Fastjson2如何进行JSON的解析和对象序列化?
- 2024-10-10 Java笔试题目分享(2)知识点总结——Json对象
- 2024-10-10 面试官问,你知道http请求怎么在你的项目中变成Java对象吗?
- 2024-10-10 Spring Boot 中使用FastJSON来替换默认的JSON数据序列方式?
你 发表评论:
欢迎- 最近发表
-
- 你真的会用 Java 中的线程池吗?多个企业级线程池工具类封装实践
- 线程池的实现原理、优点与风险、以及四种线程池实现
- Java线程池ThreadPoolExecutor实现原理剖析
- 深入分析线程池的实现原理(线程池是干嘛的)
- 一文搞懂JAVA线程池工作原理(java线程池的工作流程)
- Java线程池的工作原理(java线程池的实现原理)
- 5分钟读懂C#中TcpClient、TcpListener和Socket三个类的角色
- JVM对象的创建过程(jvm运行过程中创建的对象一般存放在方法区)
- 对象组成与Java内存模型JMM分析(java对象在内存中存储的结构)
- JVM对象内存分配详细过程(栈上分配->TLAB->老年代->Eden区)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)