google protobuf 数据类型_理解Protobuf数据格式解析

时间: 2018-12-13阅读: 2784标签: 数据

什么是protobuf?

Protobuf是Google开源的一款类似于json,XML数据交换格式,其内部数据是纯二进制格式,不依赖于语言和平台,具有简单,数据量小,快速等优点。目前用于序列化与反序列化官方支持的语言有C++,C#, GO, JAVA, PYTHON。适用于大小在1M以内的数据,因为像在移动设备平台,内存是很珍贵。

protobuf格式的特点

1. 效率高/性能好

对比XML这种文件传输格式,解析时间大大低于XML解析时间,同时空间上的开销也减少了很多。

2. 可以很方便的生成文件

例如我们在进行文件传输时并不需要自己写很多东西,仅仅定义一个proto文件即可生成所需的代码

3. 支持“向后兼容”和“向前兼容”

“向后兼容”(backward compatible),就是说,当模块B升级了后,它能够正确识别模块A发出的老版本的协议。由于老版本没有“状态”这个属性,在扩充协议时,可以考虑把“状态”属性设置成非必填 的,或者给“状态”属性设置一个缺省值即可。

“向前兼容”(forward compatible),就是说,当模块A升级了之后, 模块B能够正常识别模块A发出的新版本的协议。这时候,新增加的“状态”属性会被忽略。因此其可扩展性也很强。

4. 支持多种语言

Google官方发布的源代码中包含了C++、Java、Python三种语言,这种天生就支持三种语言的格式简直少见,下面小编会使用python来为大家进行简单的讲解。


Protobuf的使用

使用方法也比较简单:

  • 定义用于消息文件.proto
  • 使用protobuf的编译器编译消息文件
  • 使用编译好对应语言的类文件进行消息的序列化与反序列化

先来定义一个简单的消息:

message Person {
   int32 id = 1;//24
   string name = 2;//wujingchao
   string email = 3;//wujingchao92@gmail.com
}

实际的二进制消息为:

08 18 12 0a 77 75 6a 69 6e 67 63 68 61 6f 1a 16 77 75 6a 69 6e 67 63 68 61 6f 39 32 40 67 6d 61 69 6c 2e 63 6f 6d

下面就讲解这段二进制流数据是怎么组成的:

varints

一般情况下int类型都是固定4个字节,protobuf定义了一种变长的int,每个字节最高位表示后面还有没有字节,低7位就为实际的值,并且使用小端的表示方法。例如1,varint的表示方法就为:

0000 0001

是不是这样就省了三个字节。

再例如300,4字节表示为:10 0101100,varint表示为:

10101100 00000010

所以前面消息为Person的id的值为00011000,即0x18。

负数的最高位为1,如果负数也使用这种方式表示就会出现一个问题,int32总是需要5个字节,int64总是需要10个字节。

所以定义了另外一种类型:sint32,sint64。采用ZigZag编码,所有的负数都使用正数表示,计算方式:

sint32
(n << 1) ^ (n >> 31)
sint64
(n << 1) ^ (n >> 63)
Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295

使用Varint编码的类型有int32, int64, uint32, uint64, sint32, sint64, bool, enum。Java里面没有对应的无符号类型,int32与uint32一样。

Wire Type

每个消息项前面都会有对应的tag,才能解析对应的数据类型,表示tag的数据类型也是Varint。

tag的计算方式: (field_number << 3) | wire_type

每种数据类型都有对应的wire_type:

Wire 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

所以wire_type最多只能支持8种,目前有6种。

所以前面Person的id,field_number为1,wire_type为0,所以对应的tag为

1 <<< 3 | 0  = 0x08

Person的name,field_number为2,wire_type为2,所以对应的tag为

2 <<< 3 | 2 = 0x12

对应Length-delimited的wire type,后面紧跟着的Varint类型表示数据的字节数。

所以name的tag后面紧跟的0x0a表示后面的数据长度为10个字节,即"wujingchao"的UTF-8 编码或者ASCII值:

08 18 12 0a 77 75 6a 69 6e 67 63 68 61 6f 1a 16

嵌套的消息类型embedded messages与packed repeated fields也是使用这种方式表示,对应默认值的数据,是不会写进protobuf消息里面的。

packed repeated与repeated的区别在于编码方式不一样,repeated将多个属性类型与值分开存储。而packed repeated采用Length-delimited方式。下面这个是官方文档的例子:

message Test4 {
    repeated int32 d = 4 [packed=true];
}

22        // tag (field number 4, wire type 2)
06        // payload size (6 bytes)
03        // first element (varint 3)
8E 02     // second element (varint 270)
9E A7 05  // third element (varint 86942)

如果没有packed的属性是这样存储的:

20 //tag(field number 4,wire type 0)
03 //first element (varint 3)
20 //tag(field number 4,wire type 0)
8E 02//second element (varint 270)
20 //tag(field number 4,wire type 0)
9E A7 05  // third element (varint 86942)

是不是这种方式比较节省内存,所以proto3的repeated默认就是使用packed这种方式来存储。(proto2与proto3区别在于.proto的语法)。



参考链接:https://www.jianshu.com/p/e0d81a9963e9


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.fly63.com/article/detial/1579

17 款可视化工具,让你的数据更美观

想让数据变得更好看?不必成为经验丰富的数据科学家,也不必成为平面设计师。有一些能让数据从简单的表格变成多种多样的图形,地图甚至词“云”。并不是所有的工具都适合你,但这些工具确实很有用

Js数据劫持与数据代理

数据劫持即使用Object.defineProperty()实现了vue的双向绑定。先来看看它是如何实现的;proxy即代理的意思。个人理解,建立一个proxy代理对象(Proxy的实例),接受你要监听的对象和监听它的handle两个参数。

如何在Vue中使用clipboard.js复制服务端数据?

第一步 创建点击对象页面元素,并绑定业务数据。第二步 引入clipboard.js。第三步 创建ClipboardJS对象实例。第四步:替换clipboard对象实例的默认的onClick事件。

Nginx返回大长度的JSON数据被截断

1 添加Nginx参数,增加缓存字符串大小;2 遇到权限问题,原因是大文件会先缓存到/proxy-temp文件夹下面,然后再返回;修改文件夹的权限为Nginx用户

让前端监控数据采集更高效

随着业务的快速发展,我们对生产环境下的问题感知能力越来越关注。作为距离用户最近的一层,前端的表现是否可靠、稳定、好用,很大程度上决定着用户对整个产品的体验和感受。因此,对于前端的监控不容忽视。

React 中获取数据的 3 种方法及它们的优缺点

在执行 I/O 操作(例如数据提取)时,要先发送网络请求,然后等待响应,接着将响应数据保存到组件的状态,最后进行渲染。在 React 中生命周期方法、Hooks和 Suspense是获取数据的方法。

浏览器保存数据的几种方法

Web产品中很多时候需要在客户端,即浏览器中保存一些必要的数据。而面临这类需求时,你应当知悉对应的解决方案不仅仅只有一种。Cookie这是最早被使用,且至今仍被广泛采用的最简单的浏览器中保存数据方法。

链表!比数组更适合做增删操作的数据结构

链表和数组的对比:在大多数语言中,数组的大小是固定的,从数组的起点或中间添加或删除元素的成本很高,因为需要移动元素,链表中的每一个元素在内存中不是连续放置的,和它左右两侧元素是没有关系的

关于Mock.js使用

目前在做一个个人网站,写了一半没有数据填充,也不知道写啥了,就顺带学习下mockjs这个东西,官网上主要介绍它是一个可以随机生成各种类型数据,拦截ajax请求等优点。接下来就近距离结束下mockjs吧!

mock.js模拟数据

开发时,后端还没完成数据输出,前端只好写静态模拟数据。数据太长了,将数据写在js文件里,完成后挨个改url。某些逻辑复杂的代码,加入或去除模拟数据时得小心翼翼。想要尽可能还原真实的数据,要么编写更多代码,要么手动修改模拟数据

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!