一、protobuf的应用场景
在官方文档中可以看到
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
可以看到protobuf 与json相比具有
压缩比高
、解压缩速度更快的优点
二、protobuf的使用
protobuf在使用上较为复杂,过程可以分为以下几步:
-
编写java 的POJO类
-
通过工具将java的POJO类,生成protobuf数据格式下的(在idea中可以下载Pojo2Proto插件)
-
新建一个以.proto的文件,并写入Pojo2Proto插件生成数据
-
通过proto.exe程序将.proto文件编译生成java类,该JAVA类中定义有PB对该类的数据格式定义、get/set操作、解/压锁数据的方法
POJO类数据:
public class Entity {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
生成的protobuf的数据:
syntax = "proto3";
message Entity {
string key = 1;
string value = 2;
}
通过protoc.exe进行编译
在proto.exe的下载地址
https://repo1.maven.org/maven2/com/google/protobuf/protoc/
编译Entity.proto文件
protoc-3.9.2-windows-x86_64.exe ./src/main/proto/Entity.proto --java_out=./src/main/java/src/
编译后生成有
StreamWrap
类的文件,生成的StreamWrap类代码量非常大,但我们所关注的功能主要是序列化、反序列化及数据的读写操作
通过
StreamWrap类压缩数据
StreamWrap.Entity.Builder builder = StreamWrap.Entity.newBuilder();//构造数据builder.setKey("123");builder.setValue("123");StreamWrap.Entity entity = builder.build();//将该PB类转换为二进制数据格式byte[] buff = entity.toByteArray();
通过
StreamWrap
类解压缩数据
StreamWrap.Entity entity = StreamWrap.Entity.parseFrom(buff);
三、protoBuf的性能测试
模拟产生多条数据,分别对数据进行JSON格式的序列化、反序列化,并记录 序列化时间、反序列化时间、占用空间大小
再对同样的数据进行PB格式下的
序列化、反序列化,并记录序列化时间、反序列化时间、占用空间大小
通过对比两种格式下的序列化时间、反序列化时间、占用空间大小来评估PB的性能
package src.entity;
import com.google.gson.Gson;
import com.google.protobuf.InvalidProtocolBufferException;
import src.proto.StreamWrap;
import src.proto.TradeOuterClass2;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
public class Ser {
// 定义数据的条数
private static int NUM = 100000;
public static void main(String[] args) throws InterruptedException, InvalidProtocolBufferException, UnsupportedEncodingException {
List<byte[]> list = new ArrayList<>();
//构造数据
Long start1 = System.currentTimeMillis();
for (int i = 0 ; i<NUM;i++) {
TradeOuterClass2.Trade.Builder builder = TradeOuterClass2.Trade.newBuilder();
builder.setTradingDay("20191230");
builder.setSettlementGroupID("SD01");
builder.setSettleMentID("1");
builder.setParticipantID("0001");
builder.setClientID("00000001");
builder.setUserID("TECHOP");
builder.setInstrumentID("ITIT");
builder.setOrderSysID("51");
TradeOuterClass2.Trade trade = builder.build();
byte[] buff = trade.toByteArray();
System.out.println(buff.length);
if (i % 100 == 0 )
Thread.sleep(1);
list.add(buff);
}
System.out.println("ProtoBuf 数据长度:" + list.toString().getBytes().length);
System.out.println("ProtoBuf 压缩时间:" + (System.currentTimeMillis() - start1));
Gson gson = new Gson();
List<String> list2 = new ArrayList<>();
Long start2 = System.currentTimeMillis();
for (int i = 0 ; i<NUM;i++) {
Trade2 trade1 = new Trade2();
trade1.setTradingDay("20191230");
trade1.setSettlementGroupID("SD01");
trade1.setSettleMentID(1);
trade1.setParticipantID("0001");
trade1.setClientID("000000001");
trade1.setUserID("TECHOP");
trade1.setInstrumentID("ITIT");
trade1.setOrderSysID("51");
System.out.println(trade1.toString().getBytes().length);
if (i % 100 == 0 )
list2.add(gson.toJson(trade1));
}
System.out.println("JSon 数据长度:" + list2.toString().getBytes().length);
System.out.println("JSon 压缩时间:" + (System.currentTimeMillis() - start2));
long start3 = System.currentTimeMillis();
for (byte[] buff : list){
TradeOuterClass2.Trade trade = TradeOuterClass2.Trade.parseFrom(buff);
}
System.out.println("ProtoBuf 解压缩时间:" + (System.currentTimeMillis() - start3));
long start4 = System.currentTimeMillis();
for (String string : list2){
Trade trade = gson.fromJson(string,Trade.class);
}
System.out.println("JSon 解压缩时间:" + (System.currentTimeMillis() - start4));
}
}
case 1 : 对一条数据进行序列化、反序列化
|
数据大小
|
序列化时间(ms)
|
反序列化时间(ms)
|
Protobuf
|
136
|
72
|
4
|
JSON
|
235
|
25
|
3
|
case2:对10万条数据进行序列化和反序列化
|
数据大小
|
序列化时间(ms)
|
反序列化时间(ms)
|
Protobuf
|
6500071
|
490
|
176
|
JSON
|
17700058
|
662
|
361
|
经过实验对比可以发现,PB的压缩比是JSON压缩数据的
三倍(如果单条数据内容更大,会产生更大的压缩比)。
当数据量较小的情况下,JSON的序列化时间和反序列化时间是远快于的PB的,当数据条数较大的情况时,PB的序列化时间和反序列化时间则更占优势。
四、关于数据格式的一些思考
常用的数据格式包括有XML、JSON以及Protobuf,这三种数据格式都是
结构化数据,也能够完成数据的
序列化。
1、Protobuf具备更好的序列化能力,尤其是在做数据仓库的时候,PB能够更好的完成数据压缩,节省存储资源
2、在数据条数较多的情况下,PB序列化、反序列化的时间占有更大的优势
3、由于ProtoBuf在序列化数据的时候存储为二进制数据,可读性极差,因此在应用场景方面,Protobuf的应用场景较窄