完整课程入口:996 全套课程体系 | Lua 学习路径 | 幂尔框架 mirs.cn
协议 schema 定了,前后端的编解码、兼容策略、错误处理就全定了。本文聚焦三个最常纠结的 Protobuf 设计点:字段存在性(optional 语义)、多态消息(oneof)、嵌套层数,给出 996 项目场景下的取舍依据。proto2 与 proto3 的行为差异是纠结的根源,先定版本:新项目直接 proto3,历史项目沿用 proto2 不迁移。
proto3 里标量字段没有"未设置"概念——0 和"没填"解码出来都是 0。活动奖励条数 0 条与"字段缺失"在业务上不同时,要显式建模:
message QuestState {
int32 quest_id = 1;
int32 progress = 2; // 0 与缺失同义,可接受
optional bool claimed = 3; // proto3 optional:区分"未领取(false)"与"未设置"
}
proto3 的 optional(3.15+ 版本编译器支持)生成存在性检查接口,has_claimed() 可用。判断标准:字段缺失会引发业务分支的,用 optional;纯数值展示类的,接受缺省值语义。
一个消息体内"可能是 A 结果也可能是 B 结果",用 oneof 替代多个可空字段:
message BattleResult {
oneof result {
WinInfo win = 1;
LoseInfo lose = 2;
}
int32 duration = 3; // 公共字段放 oneof 外
}
oneof 保证同一条消息里结果字段只有一个被设置,解码端用 which_result() 分支,消灭了"win 和 lose 同时有值"这类脏数据。字段总数在 oneof 内共享编号空间,新增结果类型只追加编号。
嵌套层级影响解码开销与代码可读性,实测 3 层与 6 层嵌套的同量数据,解码耗时相差约 15%,编解码错误率随层级上升。约定上限 4 层,超出的公共结构上提为平级引用。三个配套纪律:字段编号分配后写进 schema 注释(谁申请的、干什么用);每条 message 头注释写触发场景;schema 文件只加不改不删,废弃字段注释保留编号防复用。协议 schema 是前后端共用的地基,这些纪律执行半年,联调返工次数会下降一个量级。