zoukankan      html  css  js  c++  java
  • ProtoBuf试用与JSON的比较

    介绍

    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 = System.currentTimeMillis();
    byte[] buff = person.toByteArray();
    System.out.println("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 (%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)
    

    结果,是可以转换成功。



    作者:xiaolei123
    链接:https://www.jianshu.com/p/b4b51b99e218
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
    DHCP "No subnet declaration for xxx (no IPv4 addresses)" 报错
    Centos安装前端开发常用软件
    kubernetes学习笔记之十:RBAC(二)
    k8s学习笔记之StorageClass+NFS
    k8s学习笔记之ConfigMap和Secret
    k8s笔记之chartmuseum搭建
    K8S集群集成harbor(1.9.3)服务并配置HTTPS
    Docker镜像仓库Harbor1.7.0搭建及配置
    Nginx自建SSL证书部署HTTPS网站
  • 原文地址:https://www.cnblogs.com/javalinux/p/14308562.html
Copyright © 2011-2022 走看看