灰度不是进度条,放的是最多赔多少
灰度放量在改造评审里经常被当成进度:已经切到 30% 了。这个说法把灰度的作用讲反了。
灰度唯一的作用是:把一次大失败换成若干次小失败。 它不提高成功率,也不让改造更快。判断一个灰度设计合不合格,只看一件事——失败发生时,损失是多少,以及发现它要多久。这两个数字答不出来,那个百分比就只是进度条。
切流能切多细:五档,越往下越可控
| 粒度 | 前置条件 | 回退速度 | 适合什么 | 代价 |
|---|---|---|---|---|
| 按接口 | 入口收口 | 秒到分钟 | 独立读接口,爆炸半径最好算 | 接口之间的耦合暴露不出来 |
| 按参数/请求头 | 同上,且内部调用能透传该标识 | 秒级 | 定向验证:指定客户、指定测试账号 | 标识一旦在链路里丢失,分流就静默失效 |
| 按用户尾号 | 请求里稳定带用户主键 | 分钟级 | 长期小流量观察 | 同一用户的会话必须始终落同一侧 |
| 按租户/客户 | 有稳定的租户归属 | 分钟级 | B 端、结算类,可控且可通知 | 大客户一次即成全量 |
| 按地域/机房 | 单元化或就近部署 | 分钟级 | 覆盖真实网络与容量差异 | 一次只挡住一类故障,且样本有偏 |
阶梯越往下,一次性能影响的范围越小、越好算,但对分流用的那个标识要求越高。这里有一个必须单独确认的隐患:内部调用是否把用户/租户标识透传到底。只要中间有一层用新对象承接、丢了这个字段,分流就会静默退化——被分到新侧的请求仍然正常返回,只是分错了人,而且没有任何报错。
我现在的固定动作是:切流前先在新侧打一条命中日志,把日志里的标识与网关侧的分流决策做逐条比对。对不上就不要放量。
放量先看放进来的是哪类操作,不是先看百分比
常见的 1% → 10% → 50% → 100% 是比例节奏,它挡不住最贵的那类问题:某种业务操作一次都没被抽到。
正确的顺序是先让每一类会出事的操作都被覆盖到,再谈比例:
- 只读接口先切。错了可立即回退,无残留。
- 幂等写其次。写坏了重放一遍能修回来。
- 非幂等写最后(支付、扣减、发券、库存)。这类切换的每一次失败都要人工补偿,所以它单独占一档,且必须配一个明确的责任人和补偿 SOP。
- 异步与定时任务再单独切。任务侧的分流经常被忘:在线流量切完了,定时任务还在按老路径批量写。
停留时长不该按日历定("观察两天"),而该由最慢的那个观测维度决定:如果一个问题只有月度结算才暴露,那两天毫无意义。先把「本次改造涉及的业务周期有哪些」列出来,取其中最长的一个作为最短观察期。
开关的三条治理规则
先给一个前置判断:2026 年不该再从零自建一套开关平台。 开源自建路线是活跃的(Unleash、Flagsmith 这类都在持续发版),接口层也已经有了厂商中立的规范 OpenFeature(CNCF 孵化项目),换实现不必改调用代码。这个领域没有事实标准的平台,但也没有必要再造一个私有标准——改造期你要的是治理规则,不是基础设施。
改造期同时存活的开关数量会失控,而开关本身就是风险源。三条硬的:
一、默认值必须走旧路径。 「新行为」要显式打开。反过来设计(默认新、可关回旧)意味着任何一次配置丢失、任何一个没读到开关的入口,都在跑未经灰度的代码。
二、开关必须有 owner 和到期日。 归属写到具体的人,不是团队。到期日在立项时就填——改造结束后没删的开关,会以「看不懂但不敢动」的形式活很多年,变成下一次改造里又一条没写在文档上的约定。
三、多个开关必须枚举组合。 两个开关就有四种状态,三个是八种。凡是没被测试过的组合,都是潜在的「A 优先 B 还是 B 优先 A」问题。做法很笨但有效:把组合列成表,逐行标注期望结果与是否验证过,写完贴在设计文档里。绝大多数开关事故的成因都是这行没标。
另外一条容易忽略的:能改开关的人,必须能看懂回滚后果。开关入口的权限收到运维或平台手里,而实际操作要求双人,不是形式主义——它的真实作用是挡住「在业务高峰前顺手点一下」。
回滚的三个必答问题
一、数据能不能退。 这是最容易在设计阶段被跳过的:新实现写下的数据,旧代码还能不能正确读?
答案取决于双向兼容还是单向兼容。数据改造那一篇里的先扩后收(新代码兼容旧格式,旧代码不保证兼容新格式)意味着一旦新代码写过数据,回滚就是单向门。所以回滚设计必须写清:旧代码读到新格式的数据时怎么办,或者明确「回滚就是认这批数据赔掉」并让业务方签字接受。后者也是合法选项,但它必须在立项时说,而不是在事故中间说。
二、在途请求怎么办。 切流那一刻正在执行的请求分三类:已进旧路径的、已进新路径的、卡在双写中间状态的。
第三类要单独设计。它的具体形态是长事务、消息已投递未消费、任务跑到一半被重启。标准是:切换后重启一次,每一笔未完成的任务都完整落在某一边,不会出现同一笔被两边各做了一半。 这一条只有演练能证明。
三、退回之后差异怎么补。 回滚不是终点。新侧在灰度期内写进去的数据、发出去的消息、变更过的状态,回滚后必须能对账并补齐或清理。
如果这一问没有答案,那这个回滚是假的——它只是把流量指回去,然后把不一致留给运维。一个能回滚的改造,回滚动作后面一定跟着一个数据核对脚本。
演练:计时、留证、换人
回滚演练要满足三个条件,否则它只是走一遍流程:
- 计时。 从决策回滚到最后一个配置生效,全过程分钟数记下来。这个数字是承诺给业务方的,不是感觉。
- 留证。 每一步的实际执行人、实际动作、与文档不一致的地方。不一致处最有价值。
- 换人执行。 由没有参与改造的人照文档做一遍。改造者本人演练通过没有任何信息量,因为他会用脑子里那些没写下来的前提。文档质量只等于陌生人能否执行。
还有一个反直觉的点:演练要挑最坏时点,不是最干净时点。 在「双写进行到一半、消息积压、部分数据是新格式」的状态下回滚,才是真实事故。在干净状态演练成功,然后线上出事时退不回去,这种案例我见过不止一次。
自动熔断的阈值怎么定
自动回滚听着很踏实,但它有两个前提:阈值可靠,且切回去不需要人工补偿。
阈值不要拍数字,从基线来。 取该接口老侧自己的历史波动范围(同一时段纵向比、隔天横向比),把触发线放在这个范围之外,而不是随手写一个整数百分比。这个范围是怎么算出来的要写清楚,否则两个月后没人知道这个阈值哪来的。
三个指标必须分开看,不能合成一个「健康度」:
| 指标 | 为什么单独 | 谁负责判 |
|---|---|---|
| 技术错误率/超时 | 最快,但假警最多 | 可自动 |
| 业务结果率(成功率、通过率) | 慢,但只有它能证明用户受损 | 必须有人盯 |
| 新旧差异率 | 灰度期特有,直接指向对不对 | 由新旧比对的脚本给 |
阈值要成对设置:触发值和恢复值之间留迟滞。只写一个值的告警会抖动,而抖动几次之后,值班的人就会开始不信它——这比没有告警更糟。
最后一句:只有纯路由层的切换适合自动回滚。 涉及数据写入的,一律人工。原因是自动机制无法处理上面第三问(差异怎么补),它能把流量退回去,把不一致留在原地。
冻结窗口:把业务日历放到方案里
改造排期最常见的低估,是只算自己的发布窗口,不算别人的。这些日期必须提前列出来并写进方案:月结与出账日、报表与对账跑批日、大促与营销活动日、上游系统的发布日(你改接口契约时他们也要发版)、以及值班最薄弱的时段(节假日前最后一个工作日不要切非幂等写)。
冻结期内不接受"就切一小段"的例外。 例外一旦开过一次,下一次就没人再拦。
这一段怎么算做完了
四条可核对:
- 从「决策回滚」到「全部生效」有一次由非改造成员完成的计时记录,且文档与实际操作无未记录的偏差。
- 所有开关的组合矩阵逐行有期望值,且每行都被验证过。
- 非幂等写单独占一档放量,且配有具名的补偿责任人和 SOP。
- 回滚动作后面有一个可执行的差异核对脚本,跑过一次。
第 1 条最常被省掉,省掉的代价通常要等到真正出事那天才付。
一点收尾
改造方案里,切流与回滚的篇幅通常是最少的,因为它最像"运维细节"。但它其实是整个方案里唯一决定你能不能在中途停下来的部分。
前面的批次怎么切、证据怎么取,决定的是这件事能不能做完;这一段决定的是,做不完的时候你能不能安全地停下来。一个没有安全停止能力的项目,评审时就该被判定为不可立项。
本文作者:Aryee 发布时间:2026-09-24 20:57
本文出处:ARYEE.cn 固定链接:https://www.aryee.cn/archives/legacy-modernization-cutover-rollback.html
转载或引用请连这一行一起带走;文中的代码与结论按当时环境成立,组件升级后请以官方文档为准。