详解Google-ProtoBuf中结构化数据的编码
本文的主要内容是google protobuf中序列化数据时用到的编码规则,但是,介绍具体的编码规则之前,我觉得有必要先简单介绍一下google protobuf。因此,本文首先会介绍一些google protobuf相关的内容,让读者朋友对google protobuf有一个初步的印象,然后,再开始进入正题—-深入浅出地介绍google protobuf中用到的编码规则。下面言归正传,开始今天的话题。
1. Google-ProtoBuf是什么
ProtoBuf,全称是Protocol Buffers, 它是谷歌内部用的一种高效的、可扩展的对结构化数据进行编码的格式规范。谷歌自己内部很多程序之间的通信协议都用了ProtoBuf。
ProtoBuf可以支持多种编程语言,目前已经C++, Java和Python,本文中所前的内容用到例子的话,会以C++为例。
2.如何得到Google-ProtoBuf
ProtoBuf在Google Code上的主页是:http://code.google.com/p/protobuf/, 感兴趣的朋友可以在这里下载ProtoBuf的源码,也可以在这里阅读ProtoBuf的详细的文档。
3. 深入浅出Google-ProtoBuf中的编码规则
(1)序列化和反序列化:
在开始本部分的内容之前,首先有必要介绍两个基本概念,一个是序列化,一个是反序列化。这两个概念的定义在网上搜一下都很多的,但大多都讲得比较晦涩,不太好理解,在这里我会用比较通俗的文字来解释,尽可能让读都朋友们一读就明白是怎么回事:
序列化:是指将结构化的数据按一定的编码规范转成指定格式的过程
反序列化:是指将转成指定格式的数据解析成原始的结构化数据的过程
举个例子,Person是一个表示人的对象类型,person是一个Person类型的对象,将person存到一个对应的XML文档中的过程就是一种序列化,而解析XML生成对应Person类型对象person的过程,就是一个反序列化的过程。在这里结构化数据指的就是Person类型的数据,一定的编码规范指的就是XML文档的规范。XML是一种简单的序列化方式,用XML序列化的好处是,XML的通用性比较好,另外,XML是一种文本格式,对人阅读比较友好,但是XML方式比较占空间,效率也不是很高。通常,比较高效的序列化都是采用二进制方式的,将要序列化的结构化数据,按一定的编码规范,转成为一串二进制的字节流存储下来,需要用的时候再从这串二进制的字节流中反序列化出对应的结构化的数据。
通过上面的介绍,我们给protobuf下一个比较正式的定义了:Google ProtoBuf是Google制定的一种用来序列化结构化数据的程序库。
(2)ProtoBuf中的编码:
1) ProtoBuf编码基础——Varints, varints是一种将一个整数序列化为一个或者多个Bytes的方法,越小的整数,使用的Bytes越少。
Varints的基本规则是:
(a) 每个Byte的最高位(msb)是标志位,如果该位为1,表示该Byte后面还有其它Byte,如果该位为0,表示该Byte是最后一个Byte。
(b)每个Byte的低7位是用来存数值的位
(c)Varints方法用Litte-Endian(小端)字节序
举个例子:300用Varints序列化的结果是1010 1100 0000 0010,运算过程如下 所示:
1010 1100 0000 0010->010 1100 000 0010(去标志位)->
000 0010 010 1100(调整字节序)-> 1 0010 1100 ->256+32+8+4=300(计算值)
2)ProtoBuf中消息的编码规则:
(a)每条消息(message)都是有一系列的key-value对组成的, key和value分别采用不同的编码方式。
(b)对某一条件消息(message)进行编码的时候,是把该消息中所有的key-value对序列化成二进制字节流;而解码的时候,解码程序读入二进制的字节流,解析出每一个key-value对,如果解码过程中遇到识别不出来的类型,直接跳过。这样的机制,保证了即使该消息添加了新的字段,也不会影响旧的编/解码程序正常工作。
(c)key由两部分组成,一部分是在定义消息时对字段的编号(field_num),另一部分是字段类型(wire_type)。字段类型定义如下表所示。
(d)key的编码方式:field_num << 3 | wire_type
(e)varint类型(wire_type=0)的编码,与第(1)部分中介绍的方法基本一致,但是int32, int64和sint32,sint64有些特别之处:int32和int64就是简单的按varints方法来编码,所以像-1、-2这样负数也会占比较多的Bytes。于是sint32和sint64采用了一种改进的方法:先采用Zigzag方法将所有的整数(正数、0和负数)一一映射到所有的无符号数上,然后再采用varints编码方法进行编码。Zigzag映射函数为:
Zigzag(n) = (n << 1) ^ (n >> 31), n为sint32时
Zigzag(n) = (n << 1) ^ (n >> 63), n为sint64时
下表是一个比较直观的映射表,这样映射后再进行编码的好处就是绝对值比较小的负数序列化后的结果占的Bytes数也会比较少。
(f)64-bit(wire_type=1)和32-bit(wire_type=5)的编码方式就比较简单了,直接在key后面跟上64bits或32bits,采用Little-Endian(小端)字节序。
(g)length-delimited(wire_type=2)的编码方式:key+length+content, key的编码方式是统一的,length采用varints编码方式,content就是由length指定的长度的Bytes。
(h)wire_type=3和4的现在已经不推荐使用了,因此这里也不再做介绍。
3)ProtoBuf编解码中字段顺序(Field order)的问题:
(a) 编码/解码与字段顺序无关,这一点由key-value机制就能保证
(b)对于未知的字段,编码的时候会把它写在序列化完的已知字段后面。
详解Google-ProtoBuf中结构化数据的编码的更多相关文章
- [转] Protobuf高效结构化数据存储格式
从公司的项目源码中看到了这个东西,觉得挺好用的,写篇博客做下小总结.下面的操作以C++为编程语言,protoc的版本为libprotoc 3.2.0. 一.Protobuf? 1. 是什么? Goo ...
- 使用python读取文本中结构化数据
需求 read some .txt file in dir and find min and max num in file. solution: echo *.txt > file.name ...
- seo之google rich-snippets丰富网页摘要结构化数据(微数据)实例代码
seo之google rich-snippets丰富网页摘要结构化数据(微数据)实例代码 网页摘要是搜索引擎搜索结果下的几行字,用户能通过网页摘要迅速了解到网页的大概内容,传统的摘要是纯文字摘要,而结 ...
- H5中使用Web Storage来存储结构化数据
在上一篇对Web Storage的介绍中,可以看到,使用Storage保存key—value对时,key.value只能是字符串,这对于简单的数据来说已经够了,但是如果需要保存更复杂的数据,比如保存类 ...
- 详解Google Chrome浏览器(操作篇)(一)
开篇概述 在上篇博客中详解Google Chrome浏览器(理论篇)一文中,主要讲解了Chrome 搜索引擎使用.Chrome安装和基本操作.Chrome 基本架构.多线程等原理性问题,这篇将重点讲解 ...
- Solr系列四:Solr(solrj 、索引API 、 结构化数据导入)
一.SolrJ介绍 1. SolrJ是什么? Solr提供的用于JAVA应用中访问solr服务API的客户端jar.在我们的应用中引入solrj: <dependency> <gro ...
- 详解Google Chrome浏览器(操作篇)(上)
开篇概述 在上篇博客中详解Google Chrome浏览器(理论篇)一文中,主要讲解了Chrome 搜索引擎使用.Chrome安装和基本操作.Chrome 基本架构.多线程等原理性问题,这篇将重点讲解 ...
- 详解google Chrome浏览器(理论篇)
注解:各位读者,经博客园工作人员反馈,hosts涉及违规问题,我暂时屏蔽了最新hosts,若已经获取最新hosts的朋友们,注意保密,不要外传.给大家带来麻烦,对此非常抱歉!!! 开篇概述 1.详解g ...
- Bigtable:一个分布式的结构化数据存储系统
Bigtable:一个分布式的结构化数据存储系统 摘要 Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据.Google的很多项目将 ...
随机推荐
- cx_Oracle安装说明
简介 cx_Oracle是用python连接oracle的驱动模块,参考文章: [cx_Oracle文档](http://cx-oracle.sourceforge.net/html/index.ht ...
- Shell学习笔记 - 运算符
一.Declare命令 1. 命令格式 declare [+/-] [选项] 变量名 其中: -: 给变量设定类型属性 +:取消变量的类型属性 2. 参数说明 -i:将变量声明为整型 -a:将变量声明 ...
- [改善Java代码]频繁插入和删除时使用LinkedList
一.分析 前面有文章分析了列表的表里方式,也就是“读”的操作.本文将介绍表的“写”操作:即插入.删除.修改动作. 二.场景 1.插入元素 列表中我们使用最多的是ArrayList,下面看看他的插入(a ...
- python截取中文字符串
python的中文处理还是比较麻烦的,utf-8的字符串的长度是1-6个字符,一不小心就会从中截断,出现所谓的乱码.下面这个函数提供了,从一段utf-8编码的字符串中,截取固定长度的字串.ord(ch ...
- BZOJ 2763
2763: [JLOI2011]飞行路线 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2073 Solved: 790[Submit][Statu ...
- getComputedStyle和currentStyle
/*alert(div.style.width)*/ //null function getstyle(obj,name){ if(obj.currentStyle) { return obj.cur ...
- Linux 命令 - rm: 删除文件和目录
命令格式 rm [OPTION]... FILE... 命令参数 -f, --force 强制删除,忽略不存在的文件,不会提示. -i, --interactive 没次删除文件时,提示用户确认. - ...
- Nginx - HTTP Configuration, Module Variables
The HTTP Core module introduces a large set of variables that you can use within the value of direct ...
- ZooKeeper(3.4.5) - 开源客户端 Curator(2.7.0) 的简单示例
一.创建会话 1. 创建会话 package com.huey.dream.demo; import org.apache.curator.framework.CuratorFramework; im ...
- Messenger实现Android IPC
当Service不需要支持并发操作时Messenger会非常有用.Messenger类使用Handler执行每个传入的消息,所有客户端的调用都按顺序运行在同一个线程上,这和AIDL是有区别的,AIDL ...