有状态编排的隐藏成本
SchemaBridge Team · 2026-02-02 · Distributed Systems, Immutable Infrastructure, Orchestration, Anti-Patterns, Reliability
为什么你那些“简单”的内部工具可能是你最大的可靠性风险,以及如何通过将不可变基础设施(Immutable Infrastructure)模式应用到应用逻辑中来化解它。
在微服务的世界里,我们痴迷于解耦。我们使用队列,采用事件驱动架构,拆分单体应用。然而,在我们的编排引擎内部,我们却常常犯下分布式系统中的大忌:我们编写有状态的循环。
在 SchemaBridge,我们最近对核心系统工作流进行了全面改造。在这个过程中,我们发现自己正面对着困扰许多工程组织的同一批反模式。这不仅仅是关于我们发布说明的故事,更是一次将不可变基础设施原则应用于应用层的案例研究。
“循环”反模式
考虑一下经典的“轮询器”:
while not deployment.is_ready():
sleep(60)
check_status()
这段代码假设了一个稳定不变的宇宙。它假设轮询进程会永远存活下去。而现实中,这正是分布式单体的产物。正如 [AWS Builders' Library] 所指出的,依赖长时间运行的同步状态会产生“僵尸”进程和不可预测的故障模式。
解决方案:递归链式调用
我们将内部的 while 循环替换为递归执行模型,这与函数式编程中使用的延续传递风格(Continuation Passing Style),或分布式事务中的Saga 模式十分相似。
- 模式: 步骤 A 不会“等待”步骤 B。步骤 A 会将步骤 B 作为一个新的、独立的工作流“派生”出来。
- 结果: 零“持有”状态。如果编排器崩溃,数据库状态(步骤 B 已排队)仍然是唯一的事实来源。
不可变逻辑:使用“内容寻址”身份进行版本控制
版本控制素来是个难题。当你更新一个工作流定义时,那些可能已经在运行的执行实例会发生什么?
- 反模式: “原地更新”。你部署了新代码,而现有的执行实例突然开始出现不同的行为。
- 行业标准: 不可变基础设施。正如我们不会通过 SSH 登录服务器去打补丁(我们会直接替换它们),我们也不应该对正在运行的工作流定义打补丁。
我们实现了规范化 DSL 哈希。
通过对我们工作流逻辑的抽象语法树(AST)进行哈希处理,我们将每个版本都视为一个唯一的、内容寻址的实体。这与 temporal.io 等现代引擎所采用的策略一致:执行身份与代码身份绑定。
锁定“上帝模式”
最后,我们解决了内部工具的安全性问题。让内部“清理”脚本拥有 root 权限是很常见的做法。这违反了最小权限原则。
我们发现,我们自己内部的“渐进式发布”工具竟然有能力覆盖关键的系统 ID。我们通过一种“Sudo”模式对此进行了锁定,只有经过密码学验证的系统参与者才能请求特定的逻辑 ID。
结论
如果你的编排依赖于 sleep(),你就是在与云对抗。
通过拥抱递归、不可变性和严格身份,我们从“基于希望”的编排走向了“基于证明”的编排。
延伸阅读: