一,应用场景
广告行业多平台之间, 会用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;
- }
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<size;i++){
- student = User.Student.newBuilder();
- student.setId(101+i);
- student.setName("赵云");
- student.setDesc("五虎将之一");
- teacher.addStudents(student.build());
- }
- List<byte[]> 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();
- }
- }
- @Test
- public void fastJsonSerialization(){
- Teacher teacher = new Teacher();
- teacher.setId(100);
- teacher.setName("刘备");
- Student student = null;
- List<Student> students = new ArrayList<>();
- for(int i=0;i<size;i++){
- student = new Student();
- student.setId(101+i);
- student.setName("赵云");
- student.setDesc("五虎将之一");
- students.add(student);
- }
- List<String> 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();
- }
- }
- <span style="white-space:pre"> </span>@Test
- public void gJsonSerialization(){
- Teacher teacher = new Teacher();
- teacher.setId(100);
- teacher.setName("刘备");
- Student student = null;
- List<Student> students = new ArrayList<>();
- for(int i=0;i<size;i++){
- student = new Student();
- student.setId(101+i);
- student.setName("赵云");
- student.setDesc("五虎将之一");
- students.add(student);
- }
- List<String> 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. 以上测试很简单, 仅供参考.
五, 参考资料
2. 序列化的几种方式