引言
本文将介绍一种新的JAVA对象序列化工具——ProtoBuf
(Protocol Buffers),它是由谷歌开发的一种高效、灵活且语言中立的结构化数据序列化协议。ProtoBuf被广泛应用于数据通信、数据存储和RPC(远程过程调用)等场景,尤其是在分布式系统和微服务架构中。经过ProtoBuf序列化后,数据体积通常显著小于JSON或XML,且占用更少的带宽和存储空间,同时解析速度更快。然而,相较于JSON和XML等文本格式,ProtoBuf的可读性较差,使用起来也相对复杂,主要是增加了开发和编译步骤(需要使用protoc编译.proto文件)。本篇将介绍如何使用JProtoBuf工具,以类似JSON的方式便捷地使用ProtoBuf。
快速入门
JProtobuf
是一个基于Java的Protobuf序列化与反序列化工具,由百度开发并开源,旨在降低Protobuf在Java项目中的使用门槛。JProtobuf
为Java开发者提供了一种更直观的方式来定义和操作Protobuf数据,无需使用.proto文件和编译工具,项目的GitHub地址如下:
JProtobuf
: https://github.com/jhunters/jprotobuf
使用JProtobuf仅需三个简单步骤,即可完成JAVA对象与Protobuf数据之间的互相转换:
- 第一步,添加Maven依赖:
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.4.23</version>
</dependency>
- 第二步,为对象添加@ProtobufClass注解:
@Data
@ProtobufClass
public class User {
private Long id;
private String name;
private String trueName;
private Integer age;
private String sex;
private Date createTime;
}
- 第三步,创建ProtobufProxy代理并进行使用:
User user = new User();
user.setId(1L);
user.setName("赵侠客");
user.setAge(29);
user.setSex("男");
user.setTrueName("公众号");
user.setCreateTime(new Date());
// 创建JProtobuf代理
Codec<User> codec = ProtobufProxy.create(User.class);
// 使用Protobuf序列化
byte[] bytes = codec.encode(user);
System.out.println(bytes.length); // 38
// 使用Protobuf反序列化
User user1 = codec.decode(bytes);
从上述代码可以看出,使用JProtobuf进行对象的序列化与JSON的序列化同样简单,经过Protobuf序列化后,字节长度为38,而JSON的字节长度为98,节省了61%。当然,并非所有对象在使用Protobuf后都能有如此显著的差距,接下来我们将比较中JSON
与大JSON
的序列化后大小差异。
大小对比
3.1 中JSON大小对比
在此部分,我们参考前文《FastJson、Jackson、Gson、Hutool,JSON解析哪家强?JMH基准测试来排行》对小JSON
、中JSON
和大JSON
的定义。我们将中JSON
定义为一个包含20个用户对象的数组,由于JProtobuf不直接支持List对象,因此需将其封装为一个Users类:
@Data
@ProtobufClass
public class Users {
List<User> users;
private Long id;
}
使用上述20个用户对象创建中JSON
:
// 20个用户模拟中JSON
List<User> userList = new ArrayList<>();
IntStream.range(0, 20).forEach(i -> {
User user2=new User();
BeanUtil.copyProperties(user,user2);
userList.add(user2);
});
users.setUsers(userList);
接下来,我们使用JProtobuf
对中JSON
进行序列化:
Codec<Users> userListCodec = ProtobufProxy.create(Users.class);
String mediumJson = JSON.toJSONString(users);
byte[] mediumProtobuf = userListCodec.encode(users);
mediumJson.getBytes().length + "\t" + mediumProtobuf.length;
// 1991 800
在中JSON
的序列化中,Protobuf序列化后的长度为800,JSON序列化后的长度为1991,Protobuf相较于JSON小了60%。
3.2 大JSON大小对比
大JSON
在这里定义为稿件正文的富文本HTML数据:
@Data
@ProtobufClass
public class Article {
private Long id;
private String author;
private Long tenantId;
private String title;
private String subTitle;
private String htmlContent;
private Date publishTime;
}
我们将模拟大JSON数据:
// 稿件正文模拟大JSON
article.setId(10000L);
article.setTenantId(10000L);
article.setAuthor("公众号:赵侠客");
article.setPublishTime(new Date());
article.setTitle(RandomUtil.randomString("主标题", 100));
article.setSubTitle(RandomUtil.randomString("副标题", 50));
// 公众号文章字符串长度为89544
article.setHtmlContent(new String(Files.readAllBytes(Paths.get("article.html"))));
使用JProtobuf
对大JSON进行序列化:
Codec<Article> articleCodec = ProtobufProxy.create(Article.class);
String bigJson = JSON.toJSONString(article);
byte[] bigProtobuf= articleCodec.encode(article);
bigJson.getBytes().length + "\t" + bigProtobuf.length;
// 94595 92826
在大JSON的情况下,Protobuf序列化的尺寸仅比JSON小2%,这可能是由于大数据量中的冗余信息占总信息大小极小,因此大数据量的JSON和Protobuf之间的差距不大。
性能对比
性能对比方面,我们参考了前面的文章《FastJson、Jackson、Gson、Hutool,JSON解析哪家强?JMH基准测试来排行,为了确保准确性,我们使用JMH基准测试,从小JSON
、中JSON
、大JSON
的序列化与反序列化六项指标进行基准测试,并将性能最突出的FastJson2与Protobuf进行对比。
百分制:
我们以FastJson2的分数为100分作为参考,比FastJson2快的得分大于100分。
缩写定义:
- SS:小JSON序列化得分
- MS:中JSON序列化得分
- BS:大JSON序列化得分
- SDS:小JSON反序列化得分
- MDS:中JSON反序列化得分
- BDS:大JSON反序列化得分
- 变化:相对序列化得分变化
4.1 小JSON序列化
工具 | 得分 | 百分制 |
---|---|---|
FastJson2 | 13561505 | 100 |
Protobuf | 12858532 | 94.8 |
可以看到,Protobuf在小对象序列化方面的性能已经接近FastJson2,表现非常优异。
4.2 中JSON序列化
工具 | 得分 | 百分制 |
---|---|---|
FastJson2 | 825644 | 100 |
Protobuf | 436635 | 52.9 |
在中JSON序列化中,Protobuf的性能约为FastJson2的一半,这可能与JProtobuf不直接支持List对象有关,因此性能未能表现得那么突出。
4.3 大JSON序列化
工具 | 得分 | 百分制 |
---|---|---|
FastJson2 | 10086 | 100 |
Protobuf | 21764 | 215.8 |
Protobuf在大对象序列化中表现优异,其性能超过FastJson2一倍以上,远超其他JSON工具。
4.4 小JSON反序列化
工具 | 百分制 | 变化 | SDS | SS |
---|---|---|---|---|
FastJson2 | 100 | -41.7% | 7921069 | 13561505 |
Protobuf | 185.3 | +14.1% | 14676712 | 12858532 |
在小对象的反序列化中,Protobuf表现出色,得分达到185.3,几乎是FastJson2的两倍,且性能比序列化时还要优越。
4.5 中JSON反序列化
工具 | 百分制 | 变化 | MDS | MS |
---|---|---|---|---|
FastJson2 | 100 | -53.3% | 385392 | 825644 |
Protobuf | 79.4 | -29.9% | 306087 | 436635 |
在数组对象的反序列化中,Protobuf的性能依然未能达到理想状态,表现不如FastJson2。
4.6 大JSON反序列化
工具 | 百分制 | 变化 | BDS | BS |
---|---|---|---|---|
FastJson2 | 100 | -35.7% | 6487 | 10086 |
Protobuf | 480.4 | +43.2% | 31163 | 21764 |
在大对象的反序列化中,Protobuf表现突出,得分高达480.4,显示出其在处理大数据量时的优势,远超FastJson2。
总结
空间
以下是Protobuf相较于JSON在空间占用上的变化:
对象 | 相比JSON |
---|---|
小JSON | -61% |
中JSON | -60% |
大JSON | -2% |
时间
Protobuf与JSON工具性能排名如下:
工具 | 排名 | 总分 | 百分制 | SS | MS | BS | SDS | MDS | BDS |
---|---|---|---|---|---|---|---|---|---|
Protobuf | 王者 | 1108.6 | 195.5 | 94.8 | 52.9 | 215.8 | 185.3 | 79.4 | 480.4 |
FastJson2 | 状元 | 567 | 100 | 100 | 100 | 72.0 | 100 | 100 | 95.0 |
FastJson | 榜眼 | 394.2 | 69.5 | 62.3 | 73.2 | 35.8 | 51.3 | 71.6 | 100 |
Jackson | 探花 | 342 | 60.3 | 42.3 | 89.7 | 100 | 27.4 | 31.3 | 51.3 |
Gson | 进士 | 188.2 | 33.2 | 8.9 | 21.5 | 43.6 | 20.7 | 25.3 | 68.2 |
Hutool | 孙山 | 42.2 | 7.4 | 3.2 | 4.6 | 7.7 | 7.3 | 5.5 | 13.9 |
结论
经过上述测试,我们可以得出Protobuf的以下结论:
- 在小数据情况下,Protobuf序列化的空间节省显著,测试中达到60%左右。
- 在大数据情况下,Protobuf的序列化空间与JSON相差无几。
- 小数据的序列化性能媲美FastJson2。
- 在数组的序列化和反序列化性能上,Protobuf整体表现较低。
- 在大数据的序列化和反序列化性能上,Protobuf明显优于所有JSON工具。
综合来看,Protobuf在序列化后的空间占用和性能上均优于JSON。因此,在RPC中将序列化和反序列化的格式由JSON改为Protobuf,将为系统性能带来显著的提升。