Protocol Buffers 介绍(二)

转载请注明出处 http://blog.csdn.net/manchew/article/details/39523197


上节讲了什么是Protocol Buffers,以及如何使用。 我们知道Protocol Buffers有着高性能,低空间占用,那么,它到底是如何做到的呢?本节就深入了解一下它的实现机制。

其实任何序列化,反序列化的东东,不过就是它们的编解码过程,那么我们看看Protocol Buffers如何在Google大牛的思想下妙笔开花。


在介绍之前,先来普及几个概念:

protocol buffers里面的字段最终编码后序列化为以key-value的形式被读取或保存。

Wire Type 用来标识我们以什么方式去解析字段的方法,主要的Wire Type有以下六种,到底Wire Type的中文名叫什么,确实也不太好翻译,哪位大婶知道好的名字,不妨说来听听。


可以看到Wire type会包含很多种字段类型,换句话讲就是,int32, int 64这些都用Varint的方式去解析,而fixed64这些都会用64-bit的这种方式去解析,说这些比较抽象,之后我会用例子来说明一下。

Key用来标识后面的数据是哪个字段的值,Key被定义为( tag << 3 ) | wire type, 这样的话key的最后面3bit就是wire type, key的值右移3bit后就得到tag的值


64-bit编解码

如果定义一个message test,代码如下:

message Test
{
    required fixed64 id = 1;
}
接下来调用

...
int main(){
    Test msg,
     msg.set_id(5);
}
...
内存模型应该是这个样子的



tag为00001,对应id这个字段,wire type为001,后面的数据解析方式为64-bit的形式,64-bit的意思是,64-bit为value的值,所以id的值为5,注意Google Protocol Buffers是按小端存取数据的。虽然后面56bit都为0,但这里还是要占用7个字节。这样显得有点儿浪费空间,前面也有讲过,protocol buffers最显著的特点就是解析快,省空间。这时,wire type为 varint的方式就派上用场了。

Varint,从名字来看,就是可变的int, 我们知道,一个int在32位机器上占4个字节,如果用varint的话就不定占4个字节了,尤其你存储的是小数据时,根本不需要4个字节来存储。

Varint把第一个字节的第一个bit赋予新的意义,如果为1表示当前的值还没有表示完,后面还有字节应该属于当前值,直到遇到首位为0的字节,才表示后面没有更多的字节了。


第一步,先转化为二进制格式,然后从后面取7bit放在低地址位,因为后面还有字节来表示当前值,所以首位给1

第二步,再从二进制格式中,取7bit放在高地址位,因为这个字节已经是最后一个字节,再没有其它的字节了,所以首位给0

这样就完成了varint类型的编码了。


了解Varint的编码过程,解码就比较容易了。

例如有message

message Test{
    required int32 a = 1;
}


另外,Protocol buffers还提供了Length-Delimited方式的wire type来编解码,这种类型主要用于value为可变长度类型时使用,比如string就是一个典型的例子。

这种方式在解码的过程中,同样先解码key,可以得知wire type,然后去取得value的值。

比如 Message

message Test {
    required string b = 2;
}
编码后的内存模型为


我们对字段b进行赋值,下图显示了解码的全过程


对Key值进行解码,得到tag=2, wire type=2; 因为当前的wire type为 length-delimited类型,所以后面紧跟着length,指明value占有用多少个字节。这里注意的时,length也是varint类型,所以length本身所占字节并不是一个字节,而可以是更多个字节,如果这时字符串很长时。


关于Google Protocol Buffers的介绍就先到这里。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Protocol Buffers(简称protobuf)是一种轻便高效的数据存储和交换格式,它的语法简单,可以使用.proto文件定义数据结构,可以通过类似编译的过程将.proto文件编译成相应语言的类文件,从而方便地进行序列化和反序列化操作。protobuf支持多种编程语言,包括Java、C++、Python等。 下面是一个protobuf的使用示例: 假设我们要定义一个Person的数据类型,包括name、id、email三个属性,可以在.proto文件定义如下: ``` syntax = "proto3"; package tutorial; message Person { string name = 1; int32 id = 2; string email = 3; } ``` 这里的syntax指定了使用的protobuf版本,1表示proto2,2表示proto3。package指定了所属的包名,message定义了数据类型,其name、id、email分别是属性名,1、2、3是属性的标识号,用于序列化和反序列化时识别不同属性。 接下来,我们可以使用protobuf的编译器将.proto文件编译成Java类文件,具体命令如下: ``` protoc --java_out=./ Person.proto ``` 其,--java_out表示输出Java类文件,./表示输出到当前目录下。 编译完成后,我们就可以在Java程序使用Person类了,如下所示: ``` import tutorial.Person; Person.Builder personBuilder = Person.newBuilder(); personBuilder.setName("Tom"); personBuilder.setId(1001); personBuilder.setEmail("tom@example.com"); Person person = personBuilder.build(); ``` 这里首先导入了Person类,然后使用Person.Builder创建一个Person对象,设置其属性值,最后通过build方法生成一个不可变的Person对象。 我们还可以将Person对象序列化成二进制格式,便于网络传输或者存储到文件,如下所示: ``` byte[] bytes = person.toByteArray(); ``` 这里的toByteArray方法将Person对象序列化成一个byte数组。 反之,我们也可以将二进制数据反序列化成Person对象,如下所示: ``` Person person = Person.parseFrom(bytes); ``` 这里的parseFrom方法将byte数组反序列化成一个Person对象。 以上就是protobuf的基本使用示例,通过protobuf,我们可以轻松地定义和序列化数据类型,方便地进行网络传输和数据存储。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值