共计 3662 个字符,预计需要花费 10 分钟才能阅读完成。
介绍
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:%sn", 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)
后果,是能够转换胜利。
起源:my.oschina.net/xiaolei123/blog/3085607
欢送关注我的微信公众号「码农解围」,分享 Python、Java、大数据、机器学习、人工智能等技术,关注码农技术晋升•职场解围•思维跃迁,20 万 + 码农成长充电第一站,陪有幻想的你一起成长