10分钟,掌握Protobuf编解码原理

一、先搞懂:Protobuf 是什么 & 核心优势

Protobuf 是谷歌开源的结构化数据序列化协议(不是编程语言),核心是「先定义数据结构,再按固定规则编解码」,对比 JSON/XML 有 3 大核心优势:

  • 体积更小:二进制编码,无冗余字符(比如 JSON 的 {}/:);
  • 速度更快:编解码无需解析字符串,直接操作二进制;
  • 跨语言 / 跨平台:定义文件(.proto)可生成多语言代码(Java/Python/Go 等)。

二、编解码核心原理(10 分钟核心)

1. 前置:Protobuf 数据结构定义(基础)

先通过一个简单的 .proto 文件理解数据结构,这是编解码的「规则蓝本」:

syntax = "proto3"; // 指定版本(proto3 更简洁)

message User {       // 定义一个「消息体」,类似编程语言的类/结构体
  int32 id = 1;      // 字段:类型 + 名称 + 字段编号(核心!编解码靠这个)
  string name = 2;
  bool is_vip = 3;
}

关键:字段编号(1/2/3)是编解码的核心标识,而非字段名(name/id),这是体积小的关键之一。

2. 编解码核心:「键值对」二进制编码规则

Protobuf 编码后的二进制数据,本质是「一系列键值对」,核心规则分 2 部分:

(1)Key 编码:Tag + Wire Type(标签 + wireType)

Key 是「字段编号 + 数据类型标识」的组合,占 1~5 个字节,计算规则:

  • 公式:Key = (字段编号 << 3) | WireType(左移 3 位,或上 wireType)
  • WireType:Protobuf 定义的「基础数据类型标识」,共 6 种核心类型,对应关系:
WireType含义对应 Protobuf 类型
0Varint(变长整数)int32/int64/bool/enum/uint32 等
2Length-delimitedstring/bytes/ 嵌套 message / 数组
164 位固定长度fixed64/uint64/double
532 位固定长度fixed32/uint32/float

👉 示例:User.id(字段编号 1,int32 对应 wireType0)Key = (1 << 3) | 0 = 8(二进制:1000)

(2)Value 编码:按 WireType 分规则

这是编解码的核心,重点讲 2 种最常用的 WireType:

① WireType 0:Varint(变长整数,核心中的核心)
  • 作用:用「1~10 个字节」表示整数,小数字占更少字节(比如 123 占 1 字节,100000 占 2 字节);
  • 编码规则:
    1. 每个字节的最高位(第 8 位) 是「续行位」:1 表示后面还有字节,0 表示最后一个字节;
    2. 每个字节的低 7 位 存储数字的实际二进制(小端序:先存低位,后存高位)。
  • 示例:编码数字 150
    • 150 的二进制:10010110
    • 拆分低 7 位:0010110(第一字节) + 1(第二字节)
    • 第一字节:0010110 | 10000000 = 1010110(十进制 174)
    • 第二字节:1 | 00000000 = 00000001(十进制 1)
    • 最终编码:0x96 0x01(十六进制),仅占 2 字节。
② WireType 2:Length-delimited(长度前缀型)
  • 作用:编码「长度不固定」的数据(字符串 / 嵌套对象 / 数组);
  • 编码规则:
    1. 先存「数据长度」(按 Varint 编码);
    2. 再存「数据的原始二进制」(比如字符串转 UTF-8 二进制)。
  • 示例:编码 User.name = "Tom"
    • 字段编号 2,string 对应 wireType2 → Key = (2 <<3)|2 = 18(0x12);
    • "Tom" 的 UTF-8 二进制:0x54 0x6F 0x6D,长度 = 3;
    • 长度 3 按 Varint 编码是 0x03;
    • 最终 Value 编码:0x03 0x54 0x6F 0x6D;
    • 整个键值对:0x12 0x03 0x54 0x6F 0x6D。
3. 完整编解码示例(把 User 序列化)

假设 User 数据:id=150,name="Tom",is_vip=true(true 在 Protobuf 中是 1)

编码过程(序列化):
  1. 编码 id=150:
    • Key:(1<<3)|0 = 8(0x08);
    • Value:150 的 Varint 编码 0x96 0x01;
    • 这部分二进制:0x08 0x96 0x01。
  2. 编码 name="Tom":
    • Key:(2<<3)|2 = 18(0x12);
    • Value:0x03 0x54 0x6F 0x6D;
    • 这部分二进制:0x12 0x03 0x54 0x6F 0x6D。
  3. 编码 is_vip=true:
    • Key:(3<<3)|0 = 24(0x18);
    • Value:1 的 Varint 编码 0x01;
    • 这部分二进制:0x18 0x01。
  4. 最终二进制:0x08 0x96 0x01 0x12 0x03 0x54 0x6F 0x6D 0x18 0x01(仅 9 字节!)。
解码过程(反序列化):
  1. 读第一个字节 0x08 → Key=8;
  2. 解析 Key:8 >> 3 = 1(字段编号),8 & 0x07 = 0(wireType0);
  3. 按 wireType0 读后续字节,解析出 Value=150 → 赋值给 id;
  4. 继续读下一个字节 0x12 → Key=18 → 字段编号 2,wireType2;
  5. 按 wireType2 先读长度(0x03),再读 3 个字节 → 解析为 "Tom" → 赋值给 name;
  6. 重复上述步骤,解析出 is_vip=true;
  7. 最终还原出完整的 User 对象。

三、核心补充(1 分钟快速记)

  1. 版本差异:proto2 支持「字段默认值 / 可选 / 必填」,proto3 更简洁(默认所有字段可选,无必填);
  2. 兼容性:字段编号一旦发布不要改!新增字段用新编号,老版本解析时会忽略未知字段,保证向前兼容;
  3. 为什么快:二进制 + 固定规则 + 无冗余解析(无需像 JSON 那样解析字符串)。

总结

  1. Protobuf 编解码核心是「按字段编号 + wireType 编 Key,按 wireType 规则编 Value」,二进制键值对结构是基础;
  2. Varint(变长整数)是最核心的编码规则,通过「续行位 + 低 7 位存数据」实现小数字省空间;
  3. Length-delimited 用于字符串 / 嵌套对象,规则是「先存长度(Varint),再存数据」。

这就是 Protobuf 编解码的核心原理,10 分钟掌握这些,就能理解它为什么体积小、速度快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值