0%

protobuf | protobuf 简单介绍

这里主要说明下面几个问题

  • protobuf 为什么传输很快
  • protobuf 为什么解析很快

来自于

结构说明

  • json
1
2
3
4
{
"id":1,
"name":"cc"
}
  • xml
1
2
<id>1</id>
<name>cc</name>
  • protobuf
1
tag1tagcc

jsonxml 都比较熟了,就不说了,而 protobuf 是直接将值进行传递,每个值之间有一个 tag 来区分对象中不同的值。

没有字段的属性描述,自然而然,传输的数据量就小很多。

IDL(Interface Description Language)

protobuf 通常有一个单独的文件进行结构描述,比如

1
2
3
4
message Person {
required int32 id = 1;
required string name = 2;
}

无论什么语言编写,接收到来自于某一方发送来的数值,就会按照上述结构进行解析,每个字段通过一个 tag 来分割。

TAG 是什么

虽然,TAG 可以分割字段数值,但是,我们有几个疑问

  • TAG 是什么
  • TAG 如何分割
  • 值如果和 TAG 一样的话,那么,又怎么区分呢

tag 是什么

tag 的获取方法如下

1
2
3
static int makeTag(final int fieldNumber,final int wireType){
return (fieldNumber << 3) | wireType;
}

还是以下面的结构举例

1
2
3
4
message Person {
required int32 id = 1;
required string name = 2;
}

其中,方法体中的 fieldNumber 就是 12。「12 可以在结构上随便赋值」

关于 wireType 是下面的选择,根据你值的类型选择不同的值。

Type Meaning Used For
0 Varint int32,int64,uint32,uint64,sint32,sint64,bool,enum
1 64-bit fixes64,sfixed64,double
2 Length-delimited string,bytes,embedded message,packer repeated fields
3 Start group groups(deprecated)
4 End group groups(deprecated)
32-bit fixed32,sfixed32,float

也就是与 000 - 010 作非运算。

tag 怎么分割

Tag 分隔符为一个字节,放置在值的前面位置。最后位置没有 tag

如果确保值与TAG 相同时,保证解析正确

Varint 编码

267 翻译成机器语言就是 00000000 00000000 00000001 00001011

我们可以发现

  • 开头的 00000000 00000000 完全不需要要
  • 00000001 00001011 翻译成 10001011 00000010
    • 将低位数据放到前面,并且,只取一个字节中的后 7bit,将剩下的 1 bit 转移到下一个字节中。
    • 并且,7 bit 中的高位 bit 只有 0,1用来表示,下一个字节是否是一起的
      • 1 表示一起,0 表示下一个字节的内容是另外的字段
    • 00000001 00001011 拆成 0 0000010 0001011 将低位数据放到前面 0001011 0000010 0 然后在最前面放置 01,按照规则编程 10001011 00000010 最后的 0 是无用 bit 舍去

Zigzag 编码

考虑 -1 的编码 11111111 11111111 11111111 11111111

type Meaning Used For
0 Varint int32,int64,uint32,uint64,sint32,sint64,bool,enum

sint32sin64 是用 Zigzag 编码表示的。

原始的带符号数 Zigzag编码后的表示
0 0
-1 1
1 2
-2 3

注意,使用 Zigzag 编码后,依然会使用 varint 进行再编码。

Tag-Length-Value

字符串。

varintzigzag 都是以传输数字为基础。

如果要传输字符就要使用 Tag-Length-Value 即,TLVTag 为分隔符,Length 采用 Varint 编码方式。

解析规则

  • 传输一个数字
    • 拿到第一个 TAG
    • 解析出 filedNumberwireType
    • 高位 1 表示下一个字节还是该数字,为 0 表示下一个字节是 tag
  • 传输一个字符
    • 拿到 TAG,知道下一个是字符
    • 解析 Length
    • 遇到 0 表示 Length 已经解析完毕
    • 按照长度读取字符串
    • 下一个就是 TAG 的值了
请我喝杯咖啡吧~