介绍

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

ProtoBuf试用与JSON的比较的更多相关文章

  1. GJM : Protobuf -NET 相比Json 传输速度只需要1/3 解析只需要1/10

    在序列化速度的跑分中,Protobuf一骑绝尘,序列化速度快,性能强,体积小,所以打算了解下这个利器 1:安装篇 谷歌官方没有提供.net的实现,所以在nuget上找了一个移植的 Nuget里搜索Pr ...

  2. 【转】参照protobuf,将json数据转换成二进制在网络中传输。

    http://blog.csdn.net/gamesofsailing/article/details/38335753?utm_source=tuicool&utm_medium=refer ...

  3. protobuf与json相互转换的方法

    google的protobuf对象转json,不能直接使用FastJson之类的工具进行转换,原因是protobuf生成对象的get方法,返回的类型有byte[],而只有String类型可以作为jso ...

  4. Protobuf有没有比JSON快5倍?用代码来击破pb性能神话

    转 http://www.sohu.com/a/136487507_505779 2017-04-26 07:58 程序设计 /58 /技术 导读:Google 的 Protocol Buffers ...

  5. 【咸鱼教程】protobuf在websocket通讯中的使用

    教程目录一 protobuf简介二 使用protobuf三 Demo下载 参考: CSDN:Egret项目中使用protobuf(protobufjs) TS项目中使用Protobuf的解决方案(ba ...

  6. Protobuf实现Android Socket通讯开发教程

    本节为您介绍Protobuf实现Android Socket通讯开发教程,因此,我们需要先了理一下protobuf 是什么? Protocol buffers是一种编码方法构造的一种有效而可扩展的格式 ...

  7. ProtoBuf 与 gRPC

    用 Protobuf 很久了,但是一直觉得很简单,所以就没有做一个总结,今天想尝试一下 gRPC,顺带就一起总结一下.ProtoBuf 是个老同志了,应该是 2010 的时候发布的,然后被广泛使用,目 ...

  8. protobuf(quickStart)

    1.简介 Protocol Buffers是Google开发一种数据描述语言,能够将数据进行序列化,可用于数据存储.通信协议等方面. 可以理解成更快.更简单.更小的JSON或者XML,区别在于Prot ...

  9. 自己DIY出来一个JSON结构化展示器

    说来也巧,这个玩意,一直都想亲手写一个,因为一直用着各种网上提供的工具,觉得这个还是有些用途,毕竟,后面的实现思路和原理不是太复杂,就是对json的遍历,然后给予不同节点类型以不同的展现风格. 我这次 ...

随机推荐

  1. rancher2中文文档地址

    rancher2中文文档地址 待办 https://docs.rancher.cn/

  2. 【资源分享】Gmod自由移动镜头脚本

    *----------------------------------------------[下载区]----------------------------------------------* ...

  3. HTML学习(17)URL

    HTML 统一资源定位器(Uniform Resource Locators) URL - 统一资源定位器 Web浏览器通过URL从Web服务器请求页面. scheme://host.domain:p ...

  4. java 中使用logback日志,并实现日志按天分类压缩保存。

    以maven项目作为构建工具为例,首先引入使用logback需要的3个依赖,需要注意使用logback是需要引入slf4j-api的,因为logback是基于slf4j的 <!--logback ...

  5. wamp使用时遇到的问题总结

    1.相关网页无法访问? 解决方案:修改配置文件,将所有的Deny from all 改为Allow from all,再重启wamp 以下列举配置文件 local host无法访问 单击wamp图标, ...

  6. opencv:自适应阈值

    #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...

  7. ACM-ICPC实验室20.2.21测试-图论(二)

    A.患者的编号 给出一个有向图,要求你输出字典序最小的拓扑排序. 常规拓扑排序是做不了的,正解是反向建图,同时用大根堆的优先队列维护,保证每次优先访问编号大的结点,再反向输出~ #include< ...

  8. linux中卸载mysql以及安装yum

    卸载mysql:https://blog.csdn.net/qq_41829904/article/details/92966943 链接2:https://www.cnblogs.com/nickn ...

  9. 吴裕雄 python 机器学习——数据预处理二元化OneHotEncoder模型

    from sklearn.preprocessing import OneHotEncoder #数据预处理二元化OneHotEncoder模型 def test_OneHotEncoder(): X ...

  10. ACM的探索之Keen On Evrything But Triangle(我觉得可以很接近啦!!)

    #include<bits/stdc++.h> using namespace std; int main() { int n,q,l,r; while(cin>>n>& ...