protobuf 学习01 简介 && 编解码

何为 ProtoBuf

我们先来看看官方文档给出的定义和描述:

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

简单来讲, ProtoBuf 是结构数据序列化[1] 方法,可简单类比于 XML[2],其具有以下特点:

  • 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序

在上例中,我们定义了:

  • 类型 string,名为 stringVal 的 optional 可选字段,字段编号为 1,此字段可出现 0 或 1 次
  • 类型 bytes,名为 bytesVal 的 optional 可选字段,字段编号为 2,此字段可出现 0 或 1 次
  • 类型 EmbeddedMessage(自定义的内嵌 message 类型),名为 embeddedExample1 的 optional 可选字段,字段编号为 3,此字段可出现 0 或 1 次
  • 类型 int32,名为 repeatedInt32Val 的 repeated 可重复字段,字段编号为 4,此字段可出现 任意多次(包括 0)
  • 类型 string,名为 repeatedStringVal 的 repeated 可重复字段,字段编号为 5,此字段可出现 任意多次(包括 0)

第二步,protoc 编译 .proto 文件生成读写接口

我们在 .proto 文件中定义了数据结构,这些数据结构是面向开发者和业务程序的,并不面向存储和传输。

当需要把这些数据进行存储或传输时,就需要将这些结构数据进行序列化、反序列化以及读写。那么如何实现呢?不用担心, ProtoBuf 将会为我们提供相应的接口代码。如何提供?答案就是通过 protoc 这个编译器。

第三步,调用接口实现序列化、反序列化以及读写
针对第一步中例1定义的 message,我们可以调用第二步中生成的接口

深入 ProtoBuf - 编码

每个字段field都可以分为标签Tag,长度length和值value

特别注意这里的 [Length] 是可选的,含义是针对不同类型的数据编码结构可能会变成 Tag - Value 的形式,如果变成这样的形式,没有了 Length 我们该如何确定 Value 的边界?答案就是 Varint 编码,在后面将详细介绍

继续深入 Tag ,Tag 由 field_numberwire_type 两个部分组成:

  • field_number: message 定义字段时指定的字段编号
  • wire_type: ProtoBuf 编码类型,根据这个类型选择不同的 Value 编码方案。

整个 Tag 采用 Varints 编码方案进行编码,Varints 编码会在后面详细介绍

     计算机中的有符号数有三种表示方法,即原码反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同 [1]  。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理

对于 ZigZag 编码的思维不难理解,既然负数的 Varints 编码效率很低,那么就将负数映射到正数,然后对映射后的正数进行 Varints 编码。解码时,解出正数之后再按映射关系映射回原来的负数。

例如我们设置 int32 val = -2。映射得到 3,那么对数字 3 进行 Varints 编码,将结果存储或发送出去。接收方接到数据后进行 Varints 解码,得到数字 3,再将 3 映射回 -2。

这里的“映射”是以移位实现的,并非存储映射表。


猜你喜欢

转载自blog.csdn.net/kuaipao19950507/article/details/107285225