一,应用场景
广告行业多平台之间, 会用http协议以post的方式传递很多上下文信息. 大平台(兼有pc和mobile),比如阿里/百度/腾讯目前都以protobuffer作为数据交换格式. 我所知道的纯移动流量平台mongo和小米,采用的json. 不能明白他们做选择时的考量是什么, 所以对两者做了简单的对比测试(当然,不一定能解决自己的困惑).
二, 案例设计
1, 测试proto协议
message Teacher{
required int32 id = 1;
required string name = 2;
//学生列表
repeated Student students = 3;
}
message Student {
required int32 id = 1;
required string name = 2;
optional string desc = 3;
}
2, 最近基本的java bean 类Teacher和Student, 属性和proto里的数据完全一致.
3, 针对以上数据格式, 做三组测试:
a) 混合数据: 如上图, 属性中既有数字, 又有字符串.
b) 纯数字测试: 把上图中的字符属性注释掉. 普通java bean也做对应处理.
c) 纯字符测试: 把上图中的数字属性注释掉.
三, 测试编码
1, proto数据格式的序列化,反序列化以及序列化后文件大小. 代码为"混合数据"的代码. 三组测试只有属性不一样, 做相应的注释就好.
@Test
public void protoSerialization() {
User.Teacher.Builder teacher = User.Teacher.newBuilder();
teacher.setId(100);
teacher.setName("刘备");
User.Student.Builder student;
for(int i=0;i listB = new ArrayList<>();
long start = System.currentTimeMillis();
for(User.Student.Builder s:teacher.getStudentsBuilderList()){
listB.add(s.build().toByteArray());
}
System.out.println("protobuffer序列化耗时:"+(System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for(byte[] b:listB){
try {
User.Student.parseFrom(b);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
System.out.println("protobuffer反序列化耗时:"+(System.currentTimeMillis() - start));
try {
FileOutputStream fileOutputStream = new FileOutputStream("E:\\temp\\teacher-proto.txt");
fileOutputStream.write(teacher.build().toByteArray());
// teacher.build().writeTo(fileOutputStream);
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
2. 阿里巴巴的fastjson工具包操作json的测试代码. 由于使用静态方法操作, 所以在junit的setUp()方法中预先加载了JSON类.
@Test
public void fastJsonSerialization(){
Teacher teacher = new Teacher();
teacher.setId(100);
teacher.setName("刘备");
Student student = null;
List students = new ArrayList<>();
for(int i=0;i jsonStr = new ArrayList<>();
long start = System.currentTimeMillis();
for(Student s:students){
jsonStr.add(JSON.toJSONString(s));
}
System.out.println("fastJson序列化耗时:"+(System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for(String str:jsonStr){
JSON.parseObject(str, Student.class);
}
System.out.println("fastJson反序列化耗时:"+(System.currentTimeMillis() - start));
teacher.setStudents(students);
String result = JSON.toJSONString(teacher);
try {
FileWriter file = new FileWriter("E:\\temp\\teacher-fast-json.txt");
file.write(result);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
3. google 的json工具包操作json的测试代码
@Test
public void gJsonSerialization(){
Teacher teacher = new Teacher();
teacher.setId(100);
teacher.setName("刘备");
Student student = null;
List students = new ArrayList<>();
for(int i=0;i jsonStr = new ArrayList<>();
Gson g = new Gson();
long start = System.currentTimeMillis();
for(Student s:students){
jsonStr.add(g.toJson(s));
}
System.out.println("gson序列化耗时:"+(System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for(String str:jsonStr){
g.fromJson(str, Student.class);
}
System.out.println("gson反序列化耗时:"+(System.currentTimeMillis() - start));
teacher.setStudents(students);
String result = new Gson().toJson(teacher);
try {
FileWriter file = new FileWriter("E:\\temp\\teacher-gson.txt");
file.write(result);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
1, 数字字符混合测试
数据格式 | 序列化耗时(ms) | 反序列化耗时(ms) | 文件大小 |
---|---|---|---|
protobuffer | 61 | 20 | 440k |
fastjson工具处理json | 64 | 60 | 781k |
gson处理json | 112 | 115 | 781k |
2, 纯数字测试
数据格式 | 序列化耗时(ms) | 反序列化耗时(ms) | 文件大小 |
---|---|---|---|
protobuffer | 30 | 24 | 74k |
fastjson工具处理json | 43 | 48 | 180k |
gson处理json | 63 | 106 | 180k |
3, 纯字符
数据格式 | 序列化耗时(ms) | 反序列化耗时(ms) | 文件大小 |
---|---|---|---|
protobuffer | 57 | 19 | 396k |
fastjson工具处理json | 45 | 62 | 630 |
gson处理json | 96 | 95 | 630 |
四, 总结
1. 测试代码每次运行虽有细微差异, 但总的来说protobuff先性能和序列化后文件大小方面表现还不错, 尤其是文件大小, 比较适合我的应用场景. 跨网传输数据就能节约不少成本. 当然, json也有自己的优势, 不是protobuff能完全取代的, 具体得看应用场景.
2. pb对数字类型的编码压缩很大, 在做proto协议涉及时可以适当考虑这一点. 当然会牺牲一些cpu时间.
3. 听说"pb对嵌套数组类型会消耗大量内存", 未经测试验证.
4. 以上测试很简单, 仅供参考.
五, 参考资料
1. Protobuffer和json深度对比.
2. 序列化的几种方式