文档章节

Java中关于protobuf的使用

h
 huahaodeoschina
发布于 2017/01/13 10:06
字数 2142
阅读 327
收藏 1

1、什么是protobuf

 

    protobuf全称Protocol Buffers,谷歌数据交换的一种格式,以二进制格式进行存储,独立于语言,平台无关,可扩展的机制,可序列化。

 

2、为什么要使用protobuffer

 

    protobuffer占用内存少,传输速率比传统的xml和json数据交换格式快,那么快在哪里呢?下面就protobuffer和json数据格式进行比较:

 

2.1 运行环境:pc机 window7 & 64位操作系统 8g内存

 

2.2数据格式准备

 

    ① protobuf定义的数据格式(student.proto)

    

    option java_package = "com.bestjike.protobuf";

    option java_outer_classname = "BestJkProtoBuf";

    message Teacher {

      repeated Student student = 1;

    }

    message  Student {

      optional string name = 1;

      optional int32 age = 2;

      optional string addr = 3;

      optional string qq = 4;

      optional string phone = 5;

      optional string nation = 6;

      optional string idcard = 7;

    }

    

    ②json要定义的数据格式

 

    {"teacher":[{"name":"hello0","age":23,"addr":"上海","qq":"126479327","phone":"15900007030","nation":"汉族","idcard":"621323199310021234"}]}

 

    2.3.测试对比

 

    分别是用100,1000,10000,100000数据进行测试,测试的效果是从空间上和时间去做对比。

 

   2.3.1. protobuf测试过程:

 

    ①cmd窗口输入以下命令生成protobuf代码

 

    D:\item\protobuf\proto>protoc.exe --java_out=./ student.proto

 

    ②测试代码如下

 

        package com.bestjike.protobuf;

        import java.io.File;

        import java.io.FileNotFoundException;

        import java.io.FileOutputStream;

        import java.io.IOException;

        import java.io.OutputStream;

        import java.util.ArrayList;

        import java.util.List;

        public class ProtoBufTest {        

            public static void main(String [] args) throws FileNotFoundException, IOException{

            long start = System.currentTimeMillis();

            int size = 100000;

            List<BestJkProtoBuf.Student> list = new ArrayList<BestJkProtoBuf.Student>();

            for (int i = 0; i < size; i++) {

                BestJkProtoBuf.Student.Builder builder = BestJkProtoBuf.Student.newBuilder();

                builder.setName("hello"+i);

                builder.setAge(23);

                builder.setAddr("上海");

                builder.setQq("126479327");

                builder.setPhone("15900007030");

                builder.setNation("汉族");

                builder.setIdcard("621323199310021234");

                BestJkProtoBuf.Student student = builder.build();

                list.add(student);

            }

            File filePath = new File("D:\\proto"+size);

            if(!filePath.exists()){

                filePath.createNewFile();

            }

            OutputStream stream = new FileOutputStream(filePath);

            BestJkProtoBuf.Teacher.Builder teaBuilder = BestJkProtoBuf.Teacher.newBuilder();

            teaBuilder.addAllStudent(list);

            BestJkProtoBuf.Teacher teacher = teaBuilder.build();

            teacher.writeTo(stream);

            System.out.println("写入文件的时间:"+(System.currentTimeMillis() - start));

            //开始对数据进行解析

            start = System.currentTimeMillis();

            byte [] buffer = teacher.toByteArray();

            teacher = BestJkProtoBuf.Teacher.parseFrom(buffer);

            for (int i = 0; i < teacher.getStudentCount(); i++) {

                System.out.println(teacher.getStudent(i).getName());

            }

            System.out.println("解析时间:"+(System.currentTimeMillis() - start));

        }

    }

 

2.3.2 json测试过程

 

package com.bestjike.protobuf;

import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.ArrayList;

import java.util.List;

import net.sf.json.JSONArray;

import net.sf.json.JSONObject;

public class JsonTest {

    public static void main(String [] args) throws IOException {

        long start = System.currentTimeMillis();

        int size = 100000;

        List<Student> list = new ArrayList<Student>();

        for (int i = 0; i < size; i++) {

            Student student = new Student();

            student.setName("hello"+i);  

            student.setAge(23);

            student.setAddr("上海");

            student.setQq("126479327");

            student.setPhone("15900007030");

            student.setNation("汉族");

            student.setIdcard("621323199310021234");

            list.add(student);

        }

        File filePath = new File("D:\\json"+size);

        if(!filePath.exists()){

            filePath.createNewFile();

        }

        PrintWriter stream = new PrintWriter(filePath);

        Teacher teacher = new Teacher();

        teacher.setStudentList(list);

        String result = Utils.toJson(teacher);

        stream.write(result);

        System.out.println(result);

        stream.close();

        System.out.println("时间:"+(System.currentTimeMillis() - start));

        start = System.currentTimeMillis();

        //解析json

        JSONObject studentList = JSONObject.fromObject(result);

        Object objectTea = studentList.get("teacher");

        JSONArray student = JSONArray.fromObject(objectTea);

        int length  = student.size();

        for (int i = 0; i < length; i++) {

            JSONObject object = JSONObject.fromObject(student.get(i));

            Student stu = (Student) JSONObject.toBean(object, Student.class);

            System.out.println(stu.getName());

        }

        System.out.println("解析时间:"+(System.currentTimeMillis() - start));

    }

}

 

2.3.3测试结果

 

    数据占用空间:

单位KB

100

1000

10000

100000

protobuf

8KB

73KB

732KB

7412KB

json

13KB

128KB

1288KB

12978KB

 

    解析数据时间:

单位毫秒MS

100

1000

10000

100000

protobuf

4MS

28MS

151MS

1111MS

json

115MS

298MS

2762MS

240332MS

 

2.3.4 结果说明

 

    测试次数超过10遍以上,以上的数值属于平均值,只是做了下大略的比较,没有考虑到最优算法,只是最平常的思维,从数据上看,无论是从空间上还是时间上protobuf是优于json的,从程序的编写程度,protobuf并没有很复杂,但是仅仅依靠上面的程序不能说明json怎么样,json解析工具也有多款:JackJson,Gson等,json通用性比较好,这一点上protobuf不能比拟的,只能说protobuf更适合适用存储,适合上面举的例子,但以上例子实际情况下是很少见的。

 

3. protobuf的深入分析

 

    3.1 protobuf消息的定义message

 

      protobuf有自己的消息定义,为.proto文件中的多个message,message中的字段filed都有修饰符,数据类型,字段名,tag以及选项。

     修饰符    数据类型  字段名    tag    选项

     optional int32  age=  1      [default=10];

     

    3.1.1 修饰符

 

    required:必须给定值,同时接受方能解析该字段值。(proto3已抛弃)

    optional:可选值,没有该值不会出现在序列化后的数据中的。

    repeated:和optional类似,只是它类似于Java中的list包含多个元素的值。

 

    3.1.2 数据类型

    

protobuf 数据类型

描述

字节 (N不固定变长)

Java语言映射

bool

布尔类型

1字节

boolean

double

64位浮点数

N

double

float

32为浮点数

N

float

int32

32位整数、

N

int

uint32

无符号32位整数

N

int

int64

64位整数

N

long

uint64

64为无符号整

N

long

sint32

32位整数,处理负数效率更高

N

int

sint64

64位整数 处理负数效率更高

N

int

fixed32

32位无符号整数

4

int

fixed64

64位无符号整数

8

long

sfixed32

32位整数、能以更高的效率处理负数

4

int

sfixed64

64为整数

8

long

string

只能处理 ASCII字符

N

String

bytes

用于处理多字节的语言字符、如中文

N

ByteString

enum

可以包含一个用户自定义的枚举类型uint32

N

enum

message

可以包含一个用户自定义的消息类型

N

class

比如:对枚举类型的字段可以这样

enum Content {

    head = 0;

    body = 1;

    tail = 2;

}

 

3.1.3 字段名

 

protobuf建议带下划线的命名方式:mobile_phone

 

3.1.4 tag值

 

序列化字段值时将会按tag值从小到大进行存储。

 

3.1.5选项

 

比如设置默认值:

optional int32   age =  1   [default=20];

常用选项:java_package,java_outer_classname,optimize_for,packed,deprecated

 

3.2 字段的存储

 

 当字段值被序列化后,数据以二进制的形式进行存储,序列化的代码如下:

 BestJkProtoBuf.Teacher.Builder teaBuilder = BestJkProtoBuf.Teacher.newBuilder();

 BestJkProtoBuf.Teacher teacher = teaBuilder.build();

 byte [] buffer = teacher.toByteArray();

序列化的字段值是以<key1,value1>,<key2,value2>的形式存在

 

3.3 字段值的编码

 

序列化后的值以key+value的形式,这里主要谈到key的编码,序列化的后的数据也有它的数据类型wire type,wire type有六种:

Wire Type Message的对应的数据类型
0 Varint:int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit :fixed64, sfixed64, double
2 Length-delimited :string, bytes, messages, packed repeated 
3 Start group groups (废弃)
4 End group groups (废弃)
5 32-bit: fixed32, sfixed32, float

key = tag << 3 | wire_type 低3位用来表示wire type,剩下的5位是tag的值。

 

 (1)varint 

 

varint为可变的整数类型,而int类型一般4个字节,下面说下varint的存储数据的原理,举个例子

optional int age  = 1 [default=10];

第一行为key,第二行value值

0

0

0

0

1

0

0

0

0

0

0

0

1

0

1 0

每一行左边的第一位最高位为标志位,为1时,表示value不止一个字节,为0时,表示value只需要一个字节,长度是由每个字节左边第一位最高位来决定的,不难看出,当tag>16时,key需要2个字节,每一个字节只用到7个位来存储值,相对于int类型的,当值大于2的28方减1为268435455时,需要5个字节来存储,因此当数字比较的时候,varint便不适合了。

同时会发现varint表示负数会很不方便,protobuf提供了sint32和sint64,varint对这两个进行编码,ZigZag先将对应的负数转换成对应的正数,而ZigZag编码规则如下:

0

0

-1

1

1

2

-2

3

2

4

......

.....

比如-3的存储

0

0

0

0

1

0

0

0

0

0

0

0

0

1

0

1

 

(2)64-bit 和 32-bit

 

由于varint对较大的数字存储并不合适,因此64-bit和32-bit编码弥补了varint的缺陷,它们是定长的。比如存储2的28方的值268435456

 

0

0

0

0

1

1

0

1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

1 0

0

0

0

 

 

3)Length-delimited 

 

length-delimited编码将value的值的长度也存储着,下面string为例,存储“best”字串时

0

0

0

0

1

0

1

0

0

0

0

0

0

1

0

0

0

1

1

0

0

0

1

0

0

1

1

0

0

1

0

1

0

1

1

1

0

0

1

1

0

1

1

1

0

1

0

0

第一行:key

第二行:value的长度5

第三行:b

第四行:e

第五行:s

第六行:t

 

4.收尾

  

    当以repeated修饰字段时,会涉及多个字段的值,因此设置选项[packed = true],只会存储一个key,否则的话每个值都会有一个key,proto3以上的版本已经优化,不需要考虑。这里不讲解proto编译器根据.proto文件如何生成对应的protobuf代码的,有兴趣的自己可以研究下。当然,这里主要说明protobuf用于java中开发,protobuf产生当然是为了解决某一类问题而产生的,protobuf是由Google开发的,最初protobuf被用来处理索引服务器的请求/响应协议,当时手动编组和解组比较繁琐,涉及到不同的协议。现在protobuf大部分应用在他们的RPC系统和数据存储系统

© 著作权归作者所有

h
粉丝 0
博文 2
码字总数 2535
作品 0
私信 提问
Netty with protobuf(一)

Netty with protobuf 这是一篇关于netty和protobuf2的文章,先来介绍一下protobuf的简单使用。网上有很多基本的protobuf的介绍,这里就不在赘述了。 protobuf官网上提供了一个例子,我们就拿...

秋风醉了
2014/07/26
1K
0
几种序列化协议(protobuf,xstream,jackjson,jdk,hessian)相关数据

别人的相关测试数据: http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking 测试纬度 序列化时间 反序列化时间 bytes大小 测试代码 准备protobuf文件 Message.proto文件代码...

SANSOM
2015/08/20
382
0
Google protobuf与Socket通信数据流

Google protobuf与Socket通信数据流,IM通信数据流; 1. protobuf的使用与集成,protobuf生成Java Proto文件? 2. android使用wire方式生成protobuf的Java文件? Protobuf Plugin for Gradle...

desaco
01/31
0
0
protobuf 2.5.0 编译jar

本机是windows环境 1. 下载 protobuf最新版本:https://code.google.com/p/protobuf/downloads/detail?name=protobuf-2.5.0.tar.gz 2. 解压后发现没有包.java下面只有一个POM.xml. 用maven吧.......

反经
2014/05/17
677
0
Java protobuf框架使用向导

高效的、可扩展的对结构化数据进行编码的格式规范 下面介绍的是使用Java ProtoBuf的基本步骤: 1.http://code.google.com/p/protobuf/downloads/list ,选择其中的win版本下载 2.下载一个pro...

hanzhankang
2014/02/19
7.2K
0

没有更多内容

加载失败,请刷新页面

加载更多

64.监控平台介绍 安装zabbix 忘记admin密码

19.1 Linux监控平台介绍 19.2 zabbix监控介绍 19.3/19.4/19.6 安装zabbix 19.5 忘记Admin密码如何做 19.1 Linux监控平台介绍: 常见开源监控软件 ~1.cacti、nagios、zabbix、smokeping、ope...

oschina130111
今天
10
0
当餐饮遇上大数据,嗯真香!

之前去开了一场会,主题是「餐饮领袖新零售峰会」。认真听完了餐饮前辈和新秀们的分享,觉得获益匪浅,把脑子里的核心纪要整理了一下,今天和大家做一个简单的分享,欢迎感兴趣的小伙伴一起交...

数澜科技
今天
7
0
DNS-over-HTTPS 的下一代是 DNS ON BLOCKCHAIN

本文作者:PETER LAI ,是 Diode 的区块链工程师。在进入软件开发领域之前,他主要是在做工商管理相关工作。Peter Lai 也是一位活跃的开源贡献者。目前,他正在与 Diode 团队一起开发基于区块...

红薯
今天
8
0
CC攻击带来的危害我们该如何防御?

随着网络的发展带给我们很多的便利,但是同时也带给我们一些网站安全问题,网络攻击就是常见的网站安全问题。其中作为站长最常见的就是CC攻击,CC攻击是网络攻击方式的一种,是一种比较常见的...

云漫网络Ruan
今天
11
0
实验分析性专业硕士提纲撰写要点

为什么您需要研究论文的提纲? 首先当您进行研究时,您需要聚集许多信息和想法,研究论文提纲可以较好地组织你的想法, 了解您研究资料的流畅度和程度。确保你写作时不会错过任何重要资料以此...

论文辅导员
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部