ProtoBuf试用与JSON的比较
介绍
ProtoBuf 是google团队开发的用于高效存储和读取结构化数据的工具。什么是结构化数据呢,正如字面上表达的,就是带有一定结构的数据。比如电话簿上有很多记录数据,每条记录包含姓名、ID、邮件、电话等,这种结构重复出现。
同类
XML、JSON 也可以用来存储此类结构化数据,但是使用ProtoBuf表示的数据能更加高效,并且将数据压缩得更小。
原理
ProtoBuf 是通过ProtoBuf编译器将与编程语言无关的特有的 .proto 后缀的数据结构文件编译成各个编程语言(Java,C/C++,Python)专用的类文件,然后通过Google提供的各个编程语言的支持库lib即可调用API。(关于proto结构体怎么编写,可自行查阅文档)
ProtoBuf编译器安装
Mac :
brew install protobuf
举个例子
1. 先创建一个proto文件
message.proto
syntax = "proto3";
message Person {
int32 id = 1;
string name = 2;
repeated Phone phone = 4;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message Phone {
string number = 1;
PhoneType type = 2;
}
}
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)
结果,是可以转换成功。
END

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
四要素落地持续交付
本文通过持续集成、自动化测试、流水线以及自动化部署几个要素介绍宜信的持续交付平台及实践。 一、什么是持续交付 持续交付(Continuous delivery,缩写为 CD),是一种软件工程方法,让软件产品的产出过程在一个短周期内完成,以保证软件可以稳定、持续的保持在随时可以发布的状况。它的目标在于让软件的编译、测试与发布变得更快更频繁。这种方式可以减少软件开发的成本与时间,减少风险。 而我对持续交付的一个较为抽象的理解是“一套软件工程方法论和许多最佳实践的集合”。方法论和实践都需要人去总结落地,所以,要想体会到持续交付的真正含义,就要在实际工作中贯彻和使用实践工具。 二、持续交付的价值 其最大的显性价值是,在实施持续交付后,能够做到在保证交付质量的前提下,加快交付速度,从而更快地得到市场反馈,推动产品的商业价值的实现。在互联网应用盛行、速度为王的今天,持续交付的价值更被突显出来。持续交付的能力,已成为评定一家互联网公司研发能力的重要指标。除显性价值外,如果站在不同角度看持续交付后的变化,我们还会发现一些隐性价值,而其中有一些影响甚至远远超过我们的预期。 1、通过快速灵活统一的环境构建...
-
下一篇
基于Spring Boot + Dubbo的全链路日志追踪(二)
一、概要 紧接上一篇,完成分析之后,就要具体的实现了。 service-a: 实现dubbo服务。 service-b: 实现web服务,并调用service-a实现的服务。 二、实现 2.1 日志采集及存储 本例直接使用【阿里云·日志服务】进行数据存储和检索,使用Aliyun Log Logback Appender进行日志收集及上传。 其实就是阿里自己实现了一个Logback Appender。当然我们也可以自己实现,比如上传至自建的ELK中。 2.2 项目中traceId生成、传递、销毁 2.2.1 traceId生成、销毁 2.2.1.1 客户端请求等触发(外部) 外部类请求触发情况,使用拦截器处理。 请求过来之后,生成traceId,并写入org.slf4j.MDC。 请求完成之后,将traceId从org.slf4j.MDC中移除。 package com.example.dubboserviceb.interceptor; import com.example.dubboserviceb.constants.Constants; import org.slf4j.MDC;...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2整合Redis,开启缓存,提高访问速度
- MySQL数据库在高并发下的优化方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7,8上快速安装Gitea,搭建Git服务器