0
点赞
收藏
分享

微信扫一扫

Protobuf 介绍与实战48:Protobuf 编码解决的是什么问题?以及Protobuf的编码结构介绍?

1  go grpc-go 相关技术专栏 总入口

2  Protobuf介绍与实战 图文专栏 文章目录

1、protobuf编码解决什么问题?

先看一个proto文件:

在这里插入图片描述

也就是说,可以使用protobuf编码按照一定的规则去存储数据类型,变量名,变量值等信息。
是存储数据信息的一种方式。

2、protobuf编码结构

既然,我们已经知道protobuf编码就是按照某种规则来存储数据类型、变量名、变量的值,

那么,至少有二个问题需要解决?

2.1、问题一:在网络IO等场景中传输时,数据类型、变量名是如何传输的?

在proto文件中,有一点跟JSON,XML不同,就是每条属性记录,都有唯一的标识号,如下图所示:
在这里插入图片描述

也就是说,客户端、服务器端两侧使用相同的proto文件,根据标识号就可以获取到相应的类型,以及变量名称了。

即,在网络IO传输中,只需要传输标识号就可以表达数据类型,变量名了。

节省了很多空间。

2.2、问题二:在网络IO等场景中传输时,变量的值,是如何编码传输的?

在proto文件中支持多种数据类型,如int32,int64,uint32,uint64,sfixed32,map,float等等;

那么,这些数据类型声明的变量的值是采用一种编码规则,还是多种呢?

如果是一种的话,从编码层面看,代码简单,实现方便。

但是,肯定会造成浪费,并没有根据具体的数据类型进行编码,编码规则统一,非差异化,编码效率是低的。

因此,应该是针对不同的数据类型采用不同的编码方案进行编码,实现不同类型的编码差异化。

TypeMeaning存储结构编码长度适合哪些数据类型
0VarintT-V变长(1-10个字节)int32,int64,uint32,uint64,sint32,sint64,bool,enum
164-bitT-V固定8个字节fixed64,sfixed64,double
2Length-delimitedT-L-V变长string,bytes,embedded messages,packed repeated fields
3Start group弃用弃用groups(deprecated)
4End group弃用弃用groups(deprecated)
532-bitT-V固定4个字节fixed32, sfixed32, float

这个表一共有6行,代表什么意思呢?

  • 这6行,代表6种编码形式。每种编码形式都有唯一的编号,
    • 如第1列的十进制形式的0,1,2,3,4,5;就是具体的编号,可以用3个bit就完全表示了。
    • 那么,编号有什么用呢?
      • 如,发送方对某个数据类型的变量按照某种编码形式进行编码时,只需要传输唯一的编号即可;如,传输的是0;
      • 接收方式接收到0后,就知道此变量是按照Varint进行编码的,那么解码时,按照Varint解码即可得到具体的值。
  • 第二列,就是每种编码形式的名称;
  • 第三列,就是每种编码的存储结构,主要分为两类T-V、T-L-V(下文解释具体含义)
  • 第四列,表示每种编码占用的字节长度
  • 第五列,表示每种编码针对的是proto文件中的哪些数据类型

如,fixed32类型的变量值,采用的是32-bit编码,用十进制5来表示32-bit编码方式;

在网络IO传输时,会将5转换为二进制形式传输。

同样,接收方接收到5后,会根据32-bit编码方式进行反向解码。

也就是说,在网络传输中,只会传输第1列的值;第2,3列不会传输的。

看第1列,类型Type,有效值为0,1,2,3,4,5;其中,3,4,已经废弃;

即,真正有效的编码形式也就0,1,2,5

https://www.cnblogs.com/onlysun/p/4569595.html
说明,无需分隔符

在这里插入图片描述

2.3、protobuf中的编码格式?或者说编码结构?一个完整的编码由那几部分组成?

通过上面的分析,我们已经知道了数据类型,变量名,变量值是如何进行编码的,

那么这三者如何组织呢,即,谁在前,谁在后;

2.3.1、常见的编码结构TLV

常用的编码格式结构,如TLV;

  • T,表示tag; 表示的是变量值的元数据信息,如,编码方式,变量值是否压缩,变量值的字节数等等
  • L,表示Length;表示传输数据的长度,相当于分隔符,告诉接收方,读取多少字节
  • V,表示有效值; 表示,传输的具体内容。

如下图所示:
在这里插入图片描述

可以看出来,在proto文件中message类型的结构体是由一个一个的Field组成的;

每个Field是由三部分组成;Tag, Length, Value

其中,
当数据类型是string, bytes, embedded messages, packed repeated fields时(即type的值为2时),Length部分才允许写。

其他数据类型,不需要写。

从编码方式看Varint,64-bit, 32-bit中已经知道Value的长度了,没必要再添加Length部分了。

具体Varint编码方式,下文再分享。

当Length不存在时,Field的组成由两部分组成,TagValue,类似于KeyValue形式。

在T-L-V中:

  • T的结构构成,第1个bit表示标志位,第2-5个bit表示proto文件中message里的标识号,第6-8个bit表示wire_type,
  • L表示V的字节数量,采用的是Varint编码
  • V传输的是具体的内容,可以采用4种编码方式:Varint编码,64-bit,Length-delimited,32-bit;(根据wire_type来采用不同的编码方式)

2.3.2、Tag结构中的wire_type结构说明(用来存储变量值的编码类型的)

再看下Tag的组成形式,如下图所示:

在这里插入图片描述

wire_type占用3个bit,可以表示7中编码类型。

实际中,只有0,1,2,5 四种编码类型可以使用,3个bit足以。

2.3.3、Tag结构中的Field_number组成说明?(用来存储标识号的)

在这里插入图片描述

2.3.4、总结

通过protobuf的编码结构,当接收方接收二进制流后,就可以获取到相应的数据,如

通过解析tag中的Field_number,来获取到proto文件中的标识号,从而获取到类型,变量名。

通过解析tag中的wire_type,来获取到变量值的编码方式,是Varint,还是64-bit, 32-bit, length-delimited方式,

然后,反向解码,就可以读取到变量值;

即,变量类型,变量名称,变量值都获取到了。

protobuf的编码结构,跟常见的TLV结构,略微不同的就是第2部分,当数据类型满足一定的条件时,非必须存在;

从而,减少了传输的数据量,是提高效率的一种方式。

protobuf里源码级别是如何实现的
https://blog.csdn.net/vvvlan/article/details/111471509

wire_type 不同值时的二进制结构
https://blog.csdn.net/u013862108/article/details/115899474?

举报

相关推荐

0 条评论