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
    ", personOut.getId(), personOut.getName());
    List<Message.Person.Phone> phoneList = personOut.getPhoneList();
    for (Message.Person.Phone phone : phoneList)
    {
        System.out.printf("手机号:%s (%s)
    ", 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)
    

    结果,是可以转换成功。

    来源:my.oschina.net/xiaolei123/blog/3085607

  • 相关阅读:
    Silverlight IIS设置
    HTTP Error 500.21
    ASP.NET中登录页验证码的实现 分类: ASP.NET 20120308 22:44 4936人阅读 评论(5) 收藏
    在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: 命名管 分类: 错误积累 20120309 09:22 6511人阅读 评论(4) 收藏
    用C求解一元二次方程的解,遇到的问题及解决办法 分类: C语言 20120313 00:26 3094人阅读 评论(0) 收藏
    推荐开发人员看的11本较具影响力书籍 分类: 转载收藏 20120311 02:03 551人阅读 评论(0) 收藏
    我的程序员之路(三) 分类: 程序人生 20120324 01:20 529人阅读 评论(0) 收藏
    我的程序员之路(二) 分类: 程序人生 20120311 00:38 848人阅读 评论(3) 收藏
    我的程序员之路(一) 分类: 程序人生 20120308 21:03 1692人阅读 评论(4) 收藏
    listBox应用
  • 原文地址:https://www.cnblogs.com/hejunlin/p/14099068.html
Copyright © 2011-2022 走看看