2、兼容性基础概念:向前兼容、向后兼容、破坏性变更、非破坏性变更
聊数据契约治理,绕不开四个基础概念。我见过不少团队,契约写得挺漂亮,一上线就炸——说白了,就是没搞懂「兼容」到底是什么意思。
今天咱们把这四个词掰开揉碎。你理解透了,后面选策略才不会跑偏。
2.1 向后兼容(Backward Compatibility)
这是最常被提起的概念。新版本的消费者,能正常处理老版本生产者发出的数据。
举个例子:你的订单服务原来只传 order_id 和 amount,后来加了 currency 字段。如果新消费者收到旧数据(没有 currency),依然能正常处理——这就是向后兼容。
核心判断标准:老数据 + 新代码 = 正常运行
我在项目中遇到过一个问题:某个团队改了字段类型,把 price 从 string 改成 number。老数据里全是字符串,新消费者一读就抛异常。嗯,这就是典型的向后不兼容。
2.2 向前兼容(Forward Compatibility)
这个概念很多人会搞反。向前兼容指的是:老版本的消费者,能正常处理新版本生产者发出的数据。
你想想看,微服务架构里,服务升级不可能同时完成。总有老服务还在跑,新数据已经发过来了。这时候老消费者能不能扛住?
核心判断标准:新数据 + 老代码 = 正常运行
我曾经踩过一个坑:上游新增了一个必填字段 user_type,下游老服务根本不知道这个字段的存在。结果反序列化时直接报错。这就是向前不兼容。
| 兼容类型 | 数据方向 | 代码方向 | 典型场景 |
|---|---|---|---|
| 向后兼容 | 老数据 | 新代码 | 消费者先升级 |
| 向前兼容 | 新数据 | 老代码 | 生产者先升级 |
2.3 破坏性变更(Breaking Change)
破坏性变更,就是改了契约之后,老消费者或老生产者直接没法用了。说白了,升级必须同步,不能独立。
常见的破坏性变更包括:
- 删除字段——消费者还在读,生产者不发了
- 重命名字段——老代码按旧名字找,找不到
- 改变字段类型——比如
int变string,序列化/反序列化全乱 - 缩小取值范围——原来允许 1-100,现在只允许 1-10,老数据超限
- 增加必填字段——老数据没有这个字段,新消费者不认
注意:破坏性变更不是不能做,而是要有计划地做。我见过最惨的一次,有人直接删了 status 字段,结果下游 12 个服务全部挂掉。那天的 on-call 电话,我到现在还记得。
2.4 非破坏性变更(Non-Breaking Change)
非破坏性变更,就是改了契约,老消费者和老生产者依然能正常工作。这是我们在数据契约治理里追求的理想状态。
常见的非破坏性变更包括:
- 增加可选字段——老数据没有这个字段,新消费者给个默认值就行
- 扩大取值范围——原来 1-10,现在 1-100,老数据都在范围内
- 增加枚举值——老消费者不认识新值,但不会崩溃(前提是代码做了兜底)
- 调整字段顺序——对于 JSON 或 Protobuf 这种不依赖顺序的格式,没问题
我的习惯:每次改契约之前,先问自己三个问题——
- 老数据还能被新代码读吗?(向后兼容)
- 新数据还能被老代码读吗?(向前兼容)
- 如果都不行,我能不能分两步走?(先加后删)
2.5 四者关系:一张图说清楚
下面这张图,是我做数据契约治理时经常拿出来看的。它把四个概念的关系画得很清楚。
2.6 实战中的判断方法
光背概念没用,得能判断。我一般用这个流程:
- 看数据方向——是老数据配新代码,还是新数据配老代码?
- 看字段变化——是增、删、改、缩?
- 看默认值——老数据缺字段时,新代码有没有兜底?
- 看序列化格式——JSON 宽松,Protobuf 严格,Avro 有模式演化规则
一句话总结:向后兼容保升级,向前兼容保回滚。两者都保住,你才能在微服务里自由地改契约。
我记得有一次,团队急着上线一个新功能,直接改了字段类型。我说不行,得先加新字段,再弃用旧字段。他们嫌麻烦。结果上线当天,老消费者全崩了。从那以后,没人再敢跳过兼容性检查。
这四个概念,是数据契约治理的基石。后面讲策略选择,你会反复用到它们。现在花点时间理解透,后面就顺了。