🕵️♂️后端面试题:gRPC 的序列化协议到底牛在哪?为什么比 JSON 快这么多?
有一次面试官问我:
❝
“你知道 gRPC 为什么比 HTTP+JSON 快吗?序列化协议是什么?压缩性能又为什么好?”
❞
我当时信心满满:“这不简单嘛,gRPC 用的是 Protobuf,体积小!”
结果面试官补刀一句:
❝
“那你能具体说说 Protobuf 为什么小?序列化是怎么实现的?”
❞
😅 那一刻空气突然安静,我感觉自己只答了个“标题党”。
这次,我们就来认真聊聊这道面试常客题。
但别担心,这不是死记硬背的八股,而是聊得明白、记得住、用得上的那种。
一、先来个总结:gRPC 为什么比 JSON 快?
一句话解释:
👉 「因为 gRPC 用的是 Protobuf(二进制序列化),比 JSON(文本序列化)更紧凑、更高效。」
| 对比项 | JSON | Protobuf |
|---|---|---|
| 数据格式 | 文本 | 二进制 |
| 数据体积 | ❌ 大(包含字段名、引号、空格) | ✅ 小(使用编号和紧凑编码) |
| 序列化速度 | ❌ 慢(字符串拼装) | ✅ 快(二进制流操作) |
| 可读性 | ✅ 可读 | ❌ 不可读 |
| 向前兼容性 | 一般 | ✅ 优秀(通过 tag 号识别) |
| Schema 定义 | 动态 | 静态(.proto 文件) |
可以这么比喻:
- JSON 是带标签的行李箱:
{"name": "Tom", "age": 18} - Protobuf 是贴编号的托运行李:
1=Tom, 2=18
看起来复杂,其实更省空间、更快上飞机 ✈️。
二、底层原理:Protobuf 到底在干什么?🤔
很多人都知道 Protobuf 是“二进制序列化”,但它到底是怎么省空间的呢?
我们先直观对比一下:
🧩 JSON 序列化结构
JSON: {"id":123, "name":"Tom"}
↑ ↑ ↑ ↑ ↑
| | | | └── 逗号
| | | └── 空格
| | └── value
| └── 引号
└── key
👆 可以看到,JSON 需要:
- 写出完整的字段名(
"id"、"name") - 加上引号、逗号、空格等格式字符
结果:「可读性高,但冗余也高。」
⚙️ Protobuf 序列化结构
Protobuf(二进制流): [08 7B 12 03 54 6F 6D]
十六进制含义:
┌────┬────┬────┬────┬──────────────┐
│ 08 │ 7B │ 12 │ 03 │ 54 6F 6D │
└────┴────┴────┴────┴──────────────┘
│ │ │ │ │
│ │ │ │ └── name 内容("Tom")
│ │ │ └── name 字段长度(3字节)
│ │ └── name 的 tag(字段编号+类型信息)
│ └── id 的值(123)
└── id 的 tag(字段编号+类型信息)
示例 .proto 文件:
message User {
int32 id = 1; // tag = 1
string name = 2; // tag = 2
}
✨ 可以看出:
- 每个字段都有个编号(
tag),来自.proto文件定义; - 数值使用 「Varint 变长编码」,小数字占用更少字节;
- 字符串用「长度 + 内容」的方式存储;
结果:**不再重复字段名、引号、格式符,自然就小得多 🚀。
✅ 对比总结:
| 项目 | JSON | Protobuf |
|---|---|---|
| 存储结构 | 文本(key:value) | 二进制(tag:value) |
| 是否包含字段名 | ✅ 是 | ❌ 否(用编号代替) |
| 字段间分隔符 | ✅ 逗号、空格 | ❌ 无 |
| 整体大小 | 较大 | 非常小 |
| 机器解析速度 | 慢(字符串处理) | 快(定长二进制) |
三、压缩性能:Protobuf 为什么还能“压上加压”?💨
大家都知道 JSON 传输常配 gzip,对吧? 但 gzip 压 JSON 时,主要在压“重复结构”(key 名、引号、空格)。
而 Protobuf:
- 🚫 没有冗余 key;
- 🚫 没有空格;
- 🚫 没有引号;
- 🚫 已经结构化得很紧凑。
结果就是:
- 「即使不压缩,也比 JSON+gzip 小;」
- 「压缩之后,效果更爆炸(压缩率高、CPU 负载低)。」
这就是为什么 gRPC 在内网调用时,延迟低、吞吐高、CPU 还省。 (没错,老板看到的“成本优化”里,Protobuf 是幕后功臣 😎)
四、兼容性:Protobuf 可不是“死板”的二进制格式 💪
如果你在 .proto 文件中新增字段:
message User {
int32 id = 1;
string name = 2;
string email = 3; // 新增字段
}
老版本客户端不认识 email 字段,会直接忽略它。 不会崩、不会错。 比 JSON 的“字段名一改就炸”要友好多了。
五、那 JSON 就没地位了吗?🤝
两者各有擅长领域:
| 场景 | 适合格式 |
|---|---|
| 外部接口(给人或第三方用) | ✅ JSON |
| 内部 RPC 通信(服务间) | ✅ Protobuf |
| 前端接口调试 | ✅ JSON |
| 高性能微服务调用 | ✅ Protobuf |
很多公司都是“「外层 HTTP + JSON,内层 gRPC + Proto」”,各取所长 👌。
六、SQL 小实验:感受下数据体积差异 📊
假设我们有张用户表:
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(200)
);
取一条数据:
SELECT * FROM user WHERE id = 1;
JSON 序列化:
{"id":1,"name":"Tom","email":"tom@example.com"}
约 70 字节。
Protobuf 序列化(示意):
08 01 12 03 54 6F 6D 1A 0F 74 6F 6D 40 65 78 61 6D 70 6C 65 2E 63 6F 6D
仅 24 字节。
「压缩率超过 65%」,想象一下高并发场景下节省的带宽和 CPU 🤑。
七、发散一下:gRPC + JSON 也能“双剑合璧” ⚔️
gRPC 也支持 JSON,比如 「gRPC-Web」(用于浏览器端)。
- 内部服务:gRPC + Protobuf(高性能)
- 外部接口:gRPC-Web + JSON(兼容性)
真正兼顾性能与易用性 ✨。
八、总结 🧠
| 对比点 | JSON | Protobuf |
|---|---|---|
| 格式类型 | 文本 | 二进制 |
| 序列化速度 | 慢 | 快 |
| 体积 | 大 | 小 |
| 压缩率 | 一般 | 优秀 |
| 可读性 | 高 | 低 |
| 兼容性 | 一般 | 优秀 |
| 适用场景 | 外部接口 | 内部 RPC |
✅ 面试高分回答模板
❝
“gRPC 使用 Protobuf 做二进制序列化,它用编号取代字段名,体积小; 采用变长编码和结构化二进制存储,使序列化更快、压缩性能更好。 相比 JSON,不仅网络传输更省带宽,CPU 压缩也更轻,延迟更低。”
❞
💬 写在最后
理解 Protobuf,不只是为了背面试题, 它体现的是一种 「工程意识」: 每一个字节、每一个 bit 都被精打细算。
如果这篇文章让你豁然开朗, 记得点个「在看」支持一下 ❤️