vault backup: 2026-05-12 21:05:58
This commit is contained in:
@@ -174,8 +174,30 @@ reserved "legacy_id", "temp_field", 20 to 25, 30;
|
||||
> [!warning] Proto2 用户注意
|
||||
> proto2 **没有** `reserved` 关键字!如果你在做 proto2 → proto3 迁移,原来依赖文档规范的 reserved 行为在 proto3 中可以真正落到代码里了——这是一大收益。
|
||||
|
||||
> [!question] 思考题
|
||||
> 如果一个字段被删除了,但**没有**做 reserved,随后同事添加了 `string new_feature = 5;`,此时旧客户端读到 `new_feature` 的值时会发生什么?它会当成哪个字段的值?
|
||||
> [!question]- 思考题 — 点击查看解析
|
||||
>
|
||||
> **问题一**:假设你在线上跑着 `GetUser` API,客户端和服务端都稳定运行了两年。现在你需要添加 `avatar_url` 字段并删除 `phone` 字段。**你能在不重启任何服务、不升级任何客户端的前提下完成这件事吗?**
|
||||
>
|
||||
> **答案**:可以,但需要正确管理 field numbers。这就是本篇笔记要讲的事。
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> **问题二**:如果一个字段被删除了,但**没有**做 reserved,随后同事添加了 `string new_feature = 5;`,此时旧客户端读到 `new_feature` 的值时会发生什么?它会当成哪个字段的值?
|
||||
>
|
||||
> **答案**:Protobuf 的二进制 wire encoding 中**不包含字段名**,只包含:
|
||||
>
|
||||
> ```
|
||||
> field_number + wire_type + value_bytes
|
||||
> ```
|
||||
>
|
||||
> 解析器完全按照 **field number 为键**来解码。它不知道 `5` 这个编号以前代表什么、现在又被分配给了谁——它只认数字。所以旧客户端会读到的字节流,将 `new_feature` 的值当成之前被删除的旧字段(原编号 5 对应的 `mobile`)的值。
|
||||
>
|
||||
> | 角色 | 服务端 proto (v2: `new_feature = 5`) | 旧客户端 proto (v1: `mobile = 5`, 无 reserved) |
|
||||
> |------|------|------|
|
||||
> | 发出去的内容 | `field_number=5, type=string, value="dark_mode"` | 期望读到手机号 |
|
||||
> | 旧客户端解读 | ❌ `"dark_mode"` → 当手机号入库 | 数据语义错位 |
|
||||
>
|
||||
> 这就是为什么笔记里那条铁律:**field number 一旦使用过,就永远不能再复用**。正确的做法是删除时做 `reserved`,让编译器在后续有人复用该编号时直接报错。
|
||||
|
||||
### 删除字段的正确姿势
|
||||
|
||||
|
||||
Reference in New Issue
Block a user