bigywater.tech
ZooKeeper · 87

用 ZooKeeper 实现配置灰度发布

灰度发布的难点不在「推送新配置」,而在「只让一部分实例用新版本、并且随时能撤」。这篇用 200 个实例做按比例放量、按实例白名单、一键回滚和错误触发的自动回滚,每一步量实际生效的实例数与耗时。

7/38/94
放量 5% / 20% / 50% 时实际切到新版本的实例数(共 200)
84 ms
回滚到 0% 时全部实例切回旧版本
95 ms
错误上报到自动回滚完成

按比例放量

实测 · 本机执行输出
  percent=  0:  0 / 200 个实例在用新版本(0.0%);收敛耗时 96.2 ms
  percent=  5:  7 / 200 个实例在用新版本(3.5%);收敛耗时 137.5 ms
  percent= 20: 38 / 200 个实例在用新版本(19.0%);收敛耗时 143.7 ms
  percent= 50: 94 / 200 个实例在用新版本(47.0%);收敛耗时 138.7 ms
  percent=100:200 / 200 个实例在用新版本(100.0%);收敛耗时 143.4 ms
  放量是单调的(5% 时的实例,20% 时仍在新版本里;20% ⊂ 50% ⊂ 100%):True

灰度判断在客户端(实例自己)做:服务端只存一个 percent 数字,所有实例用同一个哈希函数算自己的桶。这样配置中心几乎没有压力,但意味着每个客户端都要带这段逻辑。推送机制见配置中心。

单调放量

用哈希而不是随机数,放量才是单调的。如果每次放量都重新随机,就会有实例在新旧版本间来回切换。

回滚

实测 · 本机执行输出
  percent=0 后 0 个实例仍在新版本;最后一个实例切回旧版本 83.9 ms 之后

回滚就是把 percent 写回 0。因为所有实例都在 watch 同一个节点,所以回滚和放量一样快。

指定实例

实测 · 本机执行输出
  percent=0,在用新版本的实例:['inst-003', 'inst-077', 'inst-150']
  删除白名单节点后,在用新版本的实例数 = 0

错误触发的自动回滚

实测 · 本机执行输出
  放量到 20%:38 个实例在用新版本;它们之中有 8 个开始出错
  控制者在看到 5 个错误节点时(阈值 5)回滚;从第一个错误上报到所有实例回到旧版本共 95 ms;仍在用新版本的实例 = 0

实例上报错误用的是临时节点:实例死亡、会话过期后错误记录会自己消失。这里的阈值逻辑非常简化。

分步演示

灰度发布
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。200 个「实例」是同一进程里的 200 个 kazoo 连接(不是 200 个进程);版本切换只是内存里的一个布尔值,没有真正的新旧版本代码
    • 没有验证:真实业务流量下的错误率判断、按用户而非按实例灰度、服务端按比例下发(客户端不带逻辑)的方案、上千实例时的收敛时间
    京ICP备2026067502号-1京公网安备11010502063487号