ProtoBuf试用与JSON的比较

只愿长相守 提交于 2019-11-26 17:37:11

  介绍
  
  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 =http://www.senta7.net/content/?729.html System.currentTimeMillis();
  
  byte[] buff = person.toByteArray();
  
  System.out.println(www.jujinyule.com"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:%s\n", personOut.getId(), personOut.getName());
  
  List<Message.Person.Phone> phoneList = personOut.getPhoneList();
  
  for (Message.Person.Phone phone : phoneList)
  
  {
  
  System.out.printf("手机号:%s ( www.xcdeyiju.com %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)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!