Protobuf的简单介绍、使用和分析

Protobuf的简单介绍、使用和分析

 

一、protobuf是什么?

protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。后面将会有简单的demo对于这两种格式的数据转化效率的对比。但这个库目前使用还不是太流行,据说谷歌内部很多产品都有使用。

二、protobuf有什么?

Protobuf 提供了C++、java、python语言的支持,提供了windows(proto.exe)和linux平台动态编译生成proto文件对应的源文件。proto文件定义了协议数据中的实体结构(message ,field)

关键字message: 代表了实体结构,由多个消息字段(field)组成。

消息字段(field): 包括数据类型、字段名、字段规则、字段唯一标识、默认值

数据类型:常见的原子类型都支持(在FieldDescriptor::kTypeToName中有定义)

字段规则:(在FieldDescriptor::kLabelToName中定义)

required:必须初始化字段,如果没有赋值,在数据序列化时会抛出异常

optional:可选字段,可以不必初始化。

repeated:数据可以重复(相当于java 中的Array或List)

字段唯一标识:序列化和反序列化将会使用到。

默认值:在定义消息字段时可以给出默认值。

三、protobuf有什么用?

Xml、Json是目前常用的数据交换格式,它们直接使用字段名称维护序列化后类实例中字段与数据之间的映射关系,一般用字符串的形式保存在序列化后的字节流中。消息和消息的定义相对独立,可读性较好。但序列化后的数据字节很大,序列化和反序列化的时间较长,数据传输效率不高。

Protobuf和Xml、Json序列化的方式不同,采用了二进制字节的序列化方式,用字段索引和字段类型通过算法计算得到字段之前的关系映射,从而达到更高的时间效率和空间效率,特别适合对数据大小和传输速率比较敏感的场合使用。

四、Protobuf在Android上的使用

1、创建proto文件,定义消息的实体结构

2、编译proto文件生成对应的java文件

3、添加protobuf-java-2.5.0.jar到android工程

4、在android中实现对消息结构的序列化/反序列化

五、Protobuf与json的对比

1、创建product.proto文件

定义了三个Message(ProductInfo、PhoneInfo、Watch)消息结构

2、消息结构对应的java类(ProductInfo、PhoneInfo、Watch)

 

3、消息结构和java对象赋值

PhoneName:” idol3”

Price:2000

Top:1

WatchName:” tcl watch”

Price:1000

Top:1

4、JSON字符串

{"phone":{"phoneName":"idol3","price":2000,"top":1},"watch":{"watchName":"tcl wtch","top":1,"price":1000}}

5、Protobuf转化后的二进制文件

空间效率

Json:107个字节

Protobuf:32个字节

时间效率

Json序列化: 1ms ,  反序列化:0ms

Protobuf 序列化: 0ms 反序列化:0ms

 

将public List<Phone> list和repeated PhoneInfo phoneInfoList =3;都赋值为1000个PhoneInfo

空间效率

Json:4206个字节

Protobuf:1332个字节

时间效率

Json序列化: 4ms ,  反序列化:1ms

Protobuf 序列化: 1ms 反序列化:0ms

六、protobuf的简单分析

1、优缺点

优点:通过以上的时间效率和空间效率,可以看出protobuf的空间效率是JSON的2-5倍,时间效率要高,对于数据大小敏感,传输效率高的模块可以采用protobuf库

缺点:消息结构可读性不高,序列化后的字节序列为二进制序列不能简单的分析有效性;目前使用不广泛,只支持java,C++和Python;

 

2、数据序列化/反序列化

a、规则:

protobuf把消息结果message也是通过 key-value对来表示。只是其中的key是采取一定的算法计算出来的即通过每个message中每个字段(field index)和字段的数据类型进行运算得来的key = (index<<3)|type;

type类型的对应关系如下:


Type


Meaning


Used For


0


Varint


int32, int64, uint32, uint64, sint32, sint64, bool, enum


1


64-bit


fixed64, sfixed64, double


2


Length-delimited


string, bytes, embedded messages, packed repeated fields


3


Start group


groups (deprecated)


4


End group


groups (deprecated)


5


32-bit


fixed32, sfixed32, float

Value会根据数据类型的不同会有两种表现形式:

对于各种int,bool,enum类型,value就是Varint

对于string,bytes,message等等类型,value就是length+原始内容编码

Varints是一种紧凑表示数字的方法。它用一个或者多个字节表示一个数字,值越小的数字字节数越少。相对于传统的用4字节表示int32类型数字,Varints对于小于128的数值都可以用一个字节表示,大于128的数值会用更多的字节来表示,对于很大的数据则需要用5个字节来表示。

Varints算法描述: 每一个字节的最高位都是有特殊含义的,如果是1,则表示后续的字节也是该数字的一部分;如果是0,则结束

b、demo生成的的二进制文件反序列化。

第1个字节 (0A)

字段索引(index):         0A = 0001010  0A>>3 = 001 = 1

数据类型(type):           0A = 0001010&111  = 2 (String);

第2个字节 (0C)

字符串长度(length):      0E = 12;

字符串:                         0A 05 69 64 6F 6C 33 10 01 18 BD 0F

第3个字节 (0A)

因为字符串是来自phoneInfo属于嵌套类型

字段索引(index):         0A = 0001010  0A>>3 = 001 = 1

数据类型(type):           0A = 0001010&111  = 2 (String);

第4-9个字节(69 64 6F 6C 33)

字符串长度(length):    05 = 5

字符串:                       69 64 6F 6C 33 = idol3

第10个字节 (10)

字段索引(index):         10 = 00010000    10A>>3 = 0010 = 2

数据类型(type):           10 = 00010000&111  = 0 (Varints);

第11个字节  (01)

Varints:                          01 = 00001字节的最高位为0 整数结束

Value:                            1;

第12个字节(18)

字段索引(index):           18 = 00011000    18>> 00011 = 3

数据类型(type):           18 = 00011000&111  = 0 (Varints);

第13个字节(D0)

最高位为1,整数计算到下一个字节

第14个字节(0F)

最高位为0,整数计算结束

Value:为11111010000 =2000

 

C、反序列化结果

phoneinfo为

phoneName = “idol3”

top = 1

price = 2000;

同样的方法watchInfo为:

watchName = “tcl name”

top = 1

price=2000

3、时间效率

通过protobuf序列化/反序列化的过程可以得出:protobuf是通过算法生成二进制流,序列化与反序列化不需要解析相应的节点属性和多余的描述信息,所以序列化和反序列化时间效率较高。

4、空间效率

xml、json是用字段名称来确定类实例中字段之间的独立性,所以序列化后的数据多了很多描述信息,增加了序列化后的字节序列的容量。

Protobuf的序列化/反序列化过程可以得出:

protobuf是由字段索引(fieldIndex)与数据类型(type)计算(fieldIndex<<3|type)得出的key维护字段之间的映射且只占一个字节,所以相比json与xml文件,protobuf的序列化字节没有过多的key与描述符信息,所以占用空间要小很多。

七、Protobuf的源码分析

1、protobuf在java使用的序列化流程

java程序调用parserFrom(byte[] data)开始字节序列的反序列,Java程序通过调用编译生类GenerateMessage中的wirteTo()方法开始将序列化后的字节写入输出流中

 

GenerateMessage 继承AbstractMessage类,序列化最终在AbstractMesssage中完成,序列化的实现过程:

a、遍历对象中Message结构()

调用AbstractMessage类中的writeTo()方法

 

b、 序列化Message中每一个字段

调用CodeOutputStream类中的writeMessageSetExtension()方法

 

c、 对于Varints  Tag 的序列化流程:

调用CodeOutputStream类中的writeUInt32()方法

调用CodeOutputStream类中的WriteRawVarint32()方法

 

d、 对于非Varints Tag的序列化

调用CodeOutputStream类中的WriteTag()方法

 

具体的序列化实现都在CodedOutputStream中完成

2、java使用protobuf 的反序列化流程分析

java程序通过调用parserFrom(byte[] data)开始反序列化

 

具体在com.google.protobuf. AbstractParser类中实现

最后在com.google.protobuf.CodedInputStream类中完成反序列化

3、动态编译

以windows下用protoc.exe工具实现proto文件编译为例,protoc.exe是用C++实现。在控制台执行命令:

编译的流程:

检查proto的语法规则

将proto的文件中的message结构转换为GenerateMessage类的子类,并实现Builder接口。

编译流程

Main.cc中的main()方法

Command_line_interface.cc中的Run()方法

Import类中Import()

在Descriptor中完成message消息的收集和转化。

时间: 2024-10-08 22:39:44

Protobuf的简单介绍、使用和分析的相关文章

Javac源码简单分析之Javac简单介绍

一.简单介绍 javac 是java语言编程编译器.javac工具读由java语言编写的类和接口的定义,并将它们编译成字节代码的class文件. 二.源码获取 OpenJDK6源码:http://download.java.net/openjdk/jdk6/ Javac的源码就在OpenJDK源码里面. 或者在CSDN下载:http://download.csdn.net/detail/p_3er/7383741 三.Javac的包 Javac的公共入口点是com.sun.tools.javac

Zookeeper简单介绍

转自:ZooKeeper学习第一期---Zookeeper简单介绍 一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术--分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术 主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成"脏数据"的后果.这时,有人可能会说这个简单,写一个调 度算法就轻松解决了.说这句话的人,可能对分布式系统不是很了解,所以才会出现这种误解.如果这些进程全部是跑在一台机上的

TensorFlow简单介绍和在centos上的安装

##tensorflow简单介绍: TensorFlow? is an open source software library for numerical computation using data flow graphs.https://www.tensorflow.org/TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算,TensorFlow为张量从图

angular1.x的简单介绍(二)

首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块上如何创建控制器我已经在上一篇简单讲过了,现在补充一下,首先是控制器的命名,控制器的命名应遵循这样的规则:(view的模块名)业务名+Controller,如loginContrl.然后是要注意不能在controller里面进行dom的操作,只能在自定义的指令里的link方法里面操作dom. 关于a

JSF简单介绍

JSF简单介绍 一. 什么是 JSF: JavaServer Faces (JSF) 是一种用于构建 Web 应用程序的新标准 Java 框架.它提供了一种以组件为中心来开发 Java Web 用户界面的方法,从而简化了开发. JavaServer Faces于2004年三月1.0版正式提出,清楚的将Web应用程序的开发者划分了三个角色:网页设计人员.应用程序设计人员以及UI组件开发者. 从使用的角度来看,网页设计人员与应用程序设计人员能够他们所熟悉的方式开发程序,而不用侵入彼此的工作范围,而U

iOS开发多线程篇—多线程简单介绍

iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过“活动监视器”可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程

Android——DDMS简单介绍

DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务. 它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息.Logcat.广播状态信息.模拟电话呼叫.接收SMS.虚拟地理坐标等等. 一,切换DDMS视图 在左侧的Devices中,可以看到正在运行的虚拟设备. 二,DDMS各个菜单简单介绍 Debug Process 断点调试程序: 个人总结断点调试程序有2种情况.1.eclipse中的

iOS开发——网络OC篇&amp;网络爬虫简单介绍

网络爬虫简单介绍 先来看看网络爬虫的基本原理: 一个通用的网络爬虫的框架如图所示: 网络爬虫的基本工作流程如下: 1.首先选取一部分精心挑选的种子URL: 2.将这些URL放入待抓取URL队列: 3.从待抓取URL队列中取出待抓取在URL,解析DNS,并且得到主机的ip,并将URL对应的网页下载下来,存储进已下载网页库中.此外,将这些URL放进已抓取URL队列. 4.分析已抓取URL队列中的URL,分析其中的其他URL,并且将URL放入待抓取URL队列,从而进入下一个循环. 以下内容均为本人个人

深入浅出JMS(一)——JMS简单介绍

假设手机仅仅能进行实时通话,没有留言和短信功能会怎么样?一个电话打过来,正好没有来得及接上,那么这个电话要传递的信息肯定就收不到了.为什么不能先将信息存下来,当用户须要查看信息的时候再去获得信息呢?伴随着这个疑惑,短息和留言应运而生,不管手机是否开机.是否未及时接到,我们都能得到当中的信息.JMS提供了相似这种功能,本章我们将系统的学习JMS中的相关重要内容. Ø 掌握JMS基本概念及适用范围 Ø 点对点模型与公布/订阅模型的差别和使用场合 Ø 熟悉核心和通用的JMS API Ø 熟悉并理解JM