首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >protobuf的那些事

protobuf的那些事

原创
作者头像
mariolu
修改2020-04-30 22:14:33
1.5K0
修改2020-04-30 22:14:33
举报

protobuf在api接口定义中有很广泛的使用。我们设计一个api接口,往往关注一些常用的指标:压缩率(影响到传输带宽和传输时间)、压缩效率、易读性、可扩展性、支持的编码语言丰富程度。之所以protobuf得到广泛的使用,是由于在这些指标中,protobuf都得到不错的成绩或者平衡性。

一、protobuf的用途

总所周知:不同的进程通过接口传输结构体数据。结构体是以最原始的格式存储在内存中,传输这块数据还需要保持网络和主机的字节序,还要解决成员内的数据边界、结束符问题。

我们可以用用一行字符串格式,表示ipv4地址。

更复杂的结构体表示法,使用类似人脑易于理解的语言,XML语法。XML做到了人易读,但是机器执行效率低,内存空间冗余多。层级深,定位到深度很深的元素难找。

XML所见即是所得,proto则采用了更有效的二进制表达,proto的数据才用高度压缩的二进制,这些二进制通过特定的编码格式有空间效率地组织在一起。proto才用二进制编码传输和接口描述性文档 共同组成。比如说网络传输的只是一个二进制,但是根据文档对应出这个二进制是有特殊含义,比如一个结构体的key。按照双方约定的共同文档,双方达到一致的数据。

同时proto的工具包有自动生成接口代码,兼容旧代码,便于扩展新元素,读写结构体的接口

二、protobuf的使用

2.1 类型

2.1.1 基本类型:

bool, int32, float, double, string,还有自定义类型

2.1.2 复制嵌套类型

类型可以嵌套,但不像oop,没有类的继承关系

2.2 修饰符

required:必须带的字段,它的反义是optional,字段是未初始化状态,在解析的时候不会进行检查。required对代码的变动兼容性要求高些,可能让消息重新定义使用不方便,不太建议

optional:默认是0,空字符串,bool是false

repeated:0或者多个元素

2.3 保留字

reserved:序列号或者名字

保留这些位置,留给以后升级用

message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }

2.4 中括号设置默认值

设置optional的默认值

optional int32 result_per_page = 3 [default = 10];

2.5 enum设置是否忽略相同值

enum允许相同值,设置这个allow_alias=true

enum EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; } enum EnumNotAllowingAlias { UNKNOWN = 0; STARTED = 1; // RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside. }

2.6 赋值多次

如果相同的成员被赋予了多次实例,比如多次赋值同一个key,对数字和字符串,会取最后一个,对于能merge的 message,会进行合并

三、protobuf的性能和原理

得益于二进制压缩,比传统的xml和json。protobuf会有很明显的空间减少来存储传输数据。

3.1 可变长度的int

Varint使用可变长度表示数字。varint的每一个字节的最高位有特殊意思。代表连续不连续。如果是1,表示后续的字节也是varint的一部分。如果是0,表示到此为止。每个字节的其他7位用来表示真实的数字。这样做的好处是,明显对于很小的数字来说,所需要表示一个int32的字节数变少了,从1个字节到5个字节。最差的情况就是5个字节表示一个int32大数。但是从概率来看,会大概率压缩数据。

3.2 解决负数问题的zigzag编码

负数的zig-zag编码

我们知道在计算机领域,负数是用一个补码表示,负数的最高位是1,所以即使是-1这个绝对值很小的数字,在编码来看也是需要占满四个字节。protobuf才用的zig-zag编码,认为以一种正负相插的编码,能大概率地减少压缩数据。

编码器

编码后

0

0

-1

1

1

2

-2

3

2

4

3.3 key-value的组织

key:数字+wire_type,共用一个byte。byte的前5位是这个成员的位置,也就是例子中的age=1的1.wire_type是后3位。wire_type的类型可以有以下几种

message {
    int age = 1;
}

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

也就是说,第一个byte的构成方式是(field_number << 3) | wire_type。这种用fiel_number来组织结构体成员的好处是对于可选的 Field,如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field,这些特性都有助于节约消息本身的大小。

3.4 编码计算性能:

对比XML字符串的IDL, 需要做DOM解析树,需要解析字符串,需要完成词法文法分析等大量消耗 CPU 的复杂计算

而 Protobuf,将二进制序列,按照约定格式读取即可。从设置是通过几个位移操作,比xml的字符串解析,速度非常有优势。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、protobuf的用途
  • 二、protobuf的使用
    • 2.1 类型
      • 2.1.1 基本类型:
      • 2.1.2 复制嵌套类型
    • 2.2 修饰符
      • 2.3 保留字
        • 2.4 中括号设置默认值
          • 2.5 enum设置是否忽略相同值
            • 2.6 赋值多次
            • 三、protobuf的性能和原理
              • 3.1 可变长度的int
                • 3.2 解决负数问题的zigzag编码
                  • 3.3 key-value的组织
                    • 3.4 编码计算性能:
                    相关产品与服务
                    文件存储
                    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档