protobuf字段编号必须从1开始连续递增,禁止跳号、重排或删除必选字段;新增字段仅可追加末尾;需预留编号区间(reserved或bytes占位);枚举值新增须追加且不可复用旧编号。

在Kratos框架中实现接口长期兼容、避免gRPC客户端调用失败或HTTP网关映射错乱,必须严格遵循Protobuf字段编号与接口演进规范——跳过编号预留、随意重排或删除必选字段,会导致下游服务panic或解析出空值。
Protobuf字段编号必须连续且从1开始
打开api/xxx/xxx.proto文件,在message定义中,所有字段编号必须从1开始严格递增,中间不能跳号。例如:string name = 1; int32 age = 2; bool active = 3; ——这是安全的。
若写成string name = 1; bool active = 3; int32 age = 4;,则编号2被跳过,后续新增字段若误填=2,将导致旧客户端把新字段当成age解析,产生语义错乱。Protobuf二进制格式不携带字段名,只认编号。
已上线服务如需新增字段,只能追加在末尾并使用下一个可用编号,【禁止修改已有字段编号】。
兼容性字段预留策略
设计初期就要为未来扩展留出空间。在message末尾预留3个以上未使用的字段编号区间,用保留注释明确标注用途。
方法一:用reserved关键字锁定编号段
reserved 10 to 19;
reserved 100;
reserved "obsolete_field";
方法二:定义占位字段(推荐)
bytes _reserved_1 = 10;
bytes _reserved_2 = 11;
bytes _reserved_3 = 12;
这些字段不参与业务逻辑,但占用编号,防止他人误用;生成代码时会被忽略,不影响序列化体积。
注意:reserved仅对.proto编译器生效,不生成Go字段;而占位bytes字段会生成结构体成员,但业务层完全不读写,更易被团队识别为“严禁触碰”。
必选字段(required)不可删除或降级为optional
第一步:确认当前所有required字段是否仍为业务强约束。
第二步:若某字段已非必需,不能直接删掉该行,也不能改为optional —— 这会破坏wire兼容性,旧客户端发送含该字段的请求时,新服务端解析会失败或静默丢弃。
正确做法是将其标记为废弃但保留字段定义,并在注释中说明:
// @deprecated: field no longer used, kept for wire compatibility
string legacy_token = 5 [deprecated = true];
第三步:在业务逻辑中彻底忽略该字段赋值,但保留其在proto结构中的位置和编号。【删除required字段将导致gRPC解析panic】
枚举值新增必须追加,不可重排或复用旧编号
enum ErrorReason {
USER_NOT_FOUND = 0;
CONTENT_MISSING = 1;
}
当需要新增错误码时,只能追加新行:
INVALID_INPUT = 2;
RATE_LIMIT_EXCEEDED = 3;
绝对不可将CONTENT_MISSING从1改为2来“腾位置”,也不可把已废弃的USER_NOT_FOUND编号0重新分配给新枚举项——gRPC客户端依据编号反序列化,编号复用等于语义覆盖,旧客户端收到值为0的消息,会误认为是USER_NOT_FOUND而非你赋予的新含义。
已废弃的枚举值应保留定义并添加deprecated = true标记,例如:
USER_NOT_FOUND = 0 [deprecated = true];











