“胶水代码”危机:为什么分布式工作流编排如此困难
SchemaBridge Team · 2025-12-01 · Distributed Systems, Architecture, DevOps
别再把工程时间浪费在 API 管道活上。了解为什么分布式工作流编排是 2026 年扩展碎片化系统的关键。
架构瓶颈:无声的生产力杀手
作为资深开发者或架构师,你一定经历过这样的循环:一开始只是一个“简单”的集成——把 Shopify 订单同步到一套遗留 ERP。你写了一个 50 行的脚本,把它包进 Lambda 或 Cron 任务,然后上线。第一天它运行正常,第十天也正常。但随后世界变了。Shopify API 加上了速率限制。遗留 ERP 在凌晨 2:00 出现数据库锁争用。某个第三方物流服务商修改了它的 JSON 响应结构。
不知不觉间,你的“临时脚本”已经变成了关键业务基础设施。但它并不是按基础设施的标准构建的,而是按脚本的标准写出来的。它缺乏完善的错误处理,不理解“状态”的概念,也没有任何内建的韧性。它失败时是静默失败,更糟的是部分失败——让你的数据停留在损坏的状态,需要花几天人工去修复。
三个月后,那个 50 行的脚本已经变异成了 5,000 行的怪物。它现在负责重试(做得很差,还带着死循环),把日志写到三个不同的地方(其中一个已经写满),并且包含层层嵌套、掩盖真实错误的 try-except 块。你的基础设施里跑着 20 个这样的脚本。它们就是你们组织的 “胶水代码”。这不是工程,而是被动应付的管道维修。
这就是 胶水代码危机。它是工程速度的无声杀手。你不再构建能推动业务的功能,而是在构建和维护脆弱的“管道”:它们会泄漏数据、在负载下堵塞,并在凌晨 3:00 爆裂,触发告警把你最好的工程师从睡梦中叫醒。在一个由碎片化微服务和无穷无尽的 SaaS API 组成的世界里,如果没有持久编排的策略,你本质上就是在速干水泥上盖房子。一周之内看起来很坚固,但裂缝不可避免。
危机的历史根源:从 CGI-BIN 到云
要理解我们为何陷入这场危机,就必须回顾软件集成的历史。1990 年代,我们有 CGI 脚本 和 Perl。它们是小型、无状态的命令,把请求转换为响应。它们是最初的“胶水代码”。在当时它们非常出色,但从来不是为处理现代数字业务中多步骤、跨越多天的业务旅程而设计的。它们是“发出即遗忘”的工具,诞生于一个尚未永远在线、尚未全球互联的世界。
2000 年代,我们转向了 ESB(企业服务总线)——像 Tibco 或 BizTalk 这样庞大而笨重的中间件。它们功能强大,但极其复杂且昂贵。它们试图把一切集中化,结果造成了“总线架构师”这一瓶颈。每次变更都需要开委员会会议。总线最终变成了它本应解决的问题本身:单点故障,以及巨大的组织摩擦来源。
2010 年代,我们抛弃 ESB,转而拥抱 微服务。我们转向 REST API 和轻量脚本(Python、Go、Node.js)。我们以为自己获得了自由。但实际上,我们只是把复杂性从“总线”挪到了“服务之间的空间”。我们用散落在整个环境中的成千上万个微小而脆弱的脚本,替换了一个笨重的中间件。如今,复杂度相对于服务数量呈 O(N^2) 增长。我们用去中心化的混乱换掉了中心化的瓶颈。
脚本心理学:为什么我们总是选择脆弱的道路
为什么我们一直在写脚本?即使是深知分布式系统陷阱的资深工程师,也常常选择“临时脚本”而不是“持久编排器”。原因在于心理。
1. “速赢”的谬误
当业务方提出一个新的集成需求时,他们希望“昨天”就能用上。脚本感觉很快。一个小时就能写完。你会觉得很有成效,“完成了任务”。但这是虚假的生产力。你是在以未来的产能为抵押借高利贷。今天省下的时间,下个月会在调试生产环境中的部分失败时成倍地还回去。对于看重短期指标胜过长期稳定性的工程经理来说,脚本是一种令人上瘾的毒药。
2. 分布式系统中的邓宁-克鲁格效应
许多开发者认为“重试很简单”。他们觉得用一个带 sleep 计时器的 while 循环包住 API 调用就够了。他们还没有经历过真实生产环境中会发生的 重试风暴、孤儿身份 或 状态损坏。在处理故障的能力上,他们正处于“期望膨胀的顶峰”。直到凌晨 3:00 的第一次重大故障,他们才意识到分布式状态是一个需要基础设施级方案的问题。
3. 缺少更好的工作单元
直到最近,我们都缺少一个用于编排的标准工作单元。我们有“函数”,有“服务”,却没有“旅程”。SchemaBridge 引入 Durable Workflow 作为这一工作单元。它让你可以把一段多步骤旅程表达为一个状态被持久化的单一实体,因此当 worker 崩溃或重启时,正在执行的步骤会被重新领取,而不是丢失整个旅程。
失败剖析:为什么“脚本”无法扩展
一个对 10 个用户有效的脚本,在 10,000 个用户时会失败,主要有三个原因,它们都根植于分布式系统的本质。我们无法仅靠“更好的代码”解决这些问题,必须用 基础设施 来解决。
1. 部分成功问题:“半生不熟”的状态
在分布式系统中,成功不是非此即彼的。假设你的脚本执行三个步骤:1)通过 Stripe 向客户扣款。2)更新内部库存数据库。3)通过 SendGrid 发送确认邮件。如果进程在第 1 步之后崩溃,会发生什么?
客户已经被扣款,但你的库存仍标记为“有货”,用户也没有收到收据。要用裸代码解决这个问题,你必须手写复杂的 Saga 模式——本质上就是为每个脚本写一个迷你编排器。你需要检查扣款是否发生、检查库存状态、处理回滚。这些样板代码最终会占据你大部分的开发时间,而由于分布式状态本身很难,你仍然时常会做错。
2. 幂等性缺口:重试的危险
网络连接时好时坏。脚本捕获到某个 API 的超时并进行重试。但如果 API 实际上已经成功,只是响应超时了呢?没有 幂等性,你的重试就会导致重复扣款或重复发货。大多数“胶水代码”开发者会忽视这一点,直到第一次有客户被扣了 5,000 美元而不是 500 美元。
为横跨数十个服务的每一次 API 调用都加上幂等键,是一项很少有团队能始终如一做到的后勤负担。有 100 个集成,就有 100 个可能忘记加键的地方。编排引擎可以替你分担其中一部分,但要清楚是哪一部分。SchemaBridge 会对 入站 触发器去重(依据负载的内容哈希,或你提供的键),因此当提供方重发同一个 webhook 时,不会把同一段旅程启动两次。然而,出站 副作用是 at-least-once 执行的:步骤可能在崩溃后被重新执行。对于非幂等的目标,请在调用本身中传入一个由稳定业务字段(订单 ID、payment intent)派生的幂等键。
八大谬误:失败的根基
要真正理解胶水代码为何失败,我们必须回到 分布式计算的八大谬误,它们最初由 Sun Microsystems 的 Peter Deutsch 等人提出。这是每个开发者刚开始编写网络代码时都会做出的错误假设,是工程领域的“虚假天堂”:
1. 网络是可靠的:并不是。数据包会丢失,路由器会重启,线缆会被切断。在云环境中,达到一定规模后,你可以预期每天都会出现间歇性的网络故障。
2. 延迟为零:并不是。即使是最快的全球光纤网络,也会引入毫秒级的延迟,并在成千上万次调用中不断累积。这种延迟抖动且不可预测,会导致一些在本地调试时就消失的竞态条件。
3. 带宽无限:并不是。大负载会堵塞你的管道并触发超时。云服务商还有严格的带宽配额,会在毫无预警的情况下限流你的“胶水代码”。
4. 网络是安全的:并不是。中间人攻击、DNS 投毒和令牌泄露是持续存在的威胁。如果没有妥善隔离,你的“胶水代码”脚本就是窃取凭证的首要目标。
5. 拓扑不会改变:会改变。负载均衡器会迁移,节点会宕机,IP 地址会被回收。脚本里“硬编码的 IP”就是一颗定时炸弹。
6. 只有一个管理员:并不是。你受制于 AWS、Cloudflare 以及技术栈中的每一个 SaaS 提供商。他们一改 API,你的脚本就是第一个牺牲品。
7. 传输成本为零:并不是。大规模序列化和反序列化 JSON 会带来实实在在的 CPU 和内存开销。一个脚本可能会把惊人比例的时间花在 json.loads() 中反复解析负载上。
8. 网络是同构的:并不是。你的技术栈混合了 Linux、Windows、JVM、Node 和遗留 SOAP 服务。指望在这样的环境中获得一成不变的行为只是一种幻想。
技术深潜:事件溯源与持久化的可扩展性
构建持久编排引擎最困难的部分之一,是管理 状态的持久化。当大量工作流同时运行,且每个工作流都执行许多步骤时,持久化层的写入负载会迅速增长。
数据库瓶颈
一种朴素的模式——每一步都用完整状态重写一行宽而可变的 workflow_history 记录——最终会在这种负载下崩溃,因为热点行上的索引和锁争用会成为主要瓶颈。SchemaBridge 在 PostgreSQL 上把这个问题一分为二:一条会变化的小型当前状态记录,以及一份只增不减的历史。
1. 小而受保护的状态行:每次运行和每个步骤的当前状态都保存在紧凑的状态行中,通过 compare-and-set 式的条件写入进行更新(只有在没有其他人先认领的情况下,步骤才会从 DISPATCHED 进入 EXECUTING)。大负载完全不放在这些行里,而是卸载到 SeaweedFS(兼容 S3)对象存储中,并由 Valkey Streams 负责在各阶段之间投递工作。
2. 仅追加的执行跟踪:每个步骤还会追加一条不可变的跟踪记录,从而为你提供运行过程的逐步轨迹。跟踪表按月进行时间分区,因此旧分区在超过保留期后可以被低成本地整体删除,而不必逐行删除。
3. 无需重新水合的崩溃恢复:由于当前状态是持久的,worker 永远不需要在内存中重建它。如果某个 worker 在步骤中途宕机,该步骤的认领会失效,另一个 worker 会重新认领并重新执行它。这就是 at-least-once 执行,也正因如此,上文关于出站幂等性的建议才至关重要。
治理缺口:管道归谁负责?
在技术挑战之外,还有一个文化挑战:治理缺口。在传统的微服务架构中,所有权是孤岛化的。“Product Service”团队拥有产品数据库。“Shipping Service”团队拥有 FedEx 集成。
但它们之间的 桥梁 归谁?
通常谁也不管。“胶水代码”脚本由某个需要快速修复的开发者写出来,然后就被遗弃了。出了问题时,Product 团队责怪 Shipping 团队,Shipping 团队又责怪 API 提供方。对于业务流程究竟是如何连接的,没有一个中央的“唯一事实来源(Source of Truth)”。
SchemaBridge 通过让 集成成为全局资产 来解决这个问题。
- 共享应用:中心团队(DevOps 或平台工程团队)可以为 Stripe 或 Salesforce 定义工作区应用(基础 URL、认证、密钥引用),供所有团队复用。各个开发者只需把该应用拖入自己的图中,而无需重新实现连接。
- 可视化的所有权:由于逻辑是可视化的,安全团队可以审计它,产品负责人可以评审它。集成不再是一个隐藏的脚本,而是一个公开透明、可见且受管理的业务流程。
生物学隐喻:作为神经系统的软件
我们正在走向这样一个世界:软件不再是一堆静态工具的集合,而是一个 活的神经系统。在生物神经系统中,信号从手指(传感器)传到大脑(逻辑),再回到肌肉(动作)。如果路径的一部分被阻断,系统会自我调整。它有反射,也有记忆。
持久编排就是企业的神经系统。它让碎片化的服务像一个统一、协调的有机体一样运作。
1. 反射:带退避的自动重试会吸收小的“痛感”(一次瞬时的 5xx、一次限流响应、一次 HTTP 调用中断开的连接),而无需惊动大脑(开发者)。
2. 记忆:运行状态是持久化的,因此即使在重启之后——哪怕是整个集群的重启——正在执行的步骤也会被重新认领并重新执行,而不是被遗忘。由于这种重新执行是 at-least-once 的,副作用仍然需要在目标端保持幂等。
3. 感知:可视化可观测性(参见 可观测性缺口)让你实时看到每一次运行:哪个步骤正在执行、哪个失败了,以及输入和输出的负载是什么。
示例场景:无声的对账崩溃
为了理解这场危机的严重性,设想一家金融科技公司,它使用大量 Python 脚本,在内部总账与多家合作银行的 API 之间对每日交易进行对账。
故障
设想某个周五,一家合作银行更换了其对账 API 的认证方案。Python 脚本并没有崩溃;它只是被拒绝,捕获了异常,然后“静默地”把当天的对账标记为“Pending”。由于没有可视化仪表盘,这个故障几天都无人察觉。
等到有人终于去看时,总账与银行之间已经累积了巨大且仍在扩大的差异。审计人员被拉进来,管理层收到通报,工程团队不得不花好几天手工重建对账时间线。这个脚本在“理想路径”上运行完美,但它没有任何“自愈”能力,也没有任何“可视化的真相”。
SchemaBridge 方案
现在设想同样的对账逻辑运行在 SchemaBridge 上。当类似的认证变更发生时,Gateway vertex(发往银行的出站 HTTP 调用)会失败,该运行会在监控中变红,被拒绝的响应附在失败的步骤上。SchemaBridge 本身不会主动通知任何人,但团队可以让这种情况变得醒目:把 Gateway 的 clientErrorMode 设置为 complete 后,被拒绝的(4xx)调用会变成一个 invalid_input 结果,团队可以据此路由,直接接入一个 Slack 通知步骤。无论哪种方式,故障都会在当天被发现,而不是几天之后。团队修复凭证,在失败的步骤上点击 Retry(重试),对账便会从中断处继续,而无需手工重建。
实现无差错编排的推荐资源
如果你想精通持久执行之道,我们推荐以下精选阅读清单:
- Designing Data-Intensive Applications:Martin Kleppmann 著。现代分布式系统的圣经。
- Saga 模式白皮书:1987 年的原始研究,至今仍定义着分布式事务。
- Reactive Design Patterns:Roland Kuhn 著。构建有韧性的消息驱动架构的必读之作。
- Distributed Systems for Fun and Profit:Mikito Takada 撰写的必读在线指南。
- SchemaBridge 文档:我们自己对 Gateway 和 Spawner 模式的深度解析。
结论:架构师的新使命
胶水代码危机是一次转型的症状。我们正在从“孤岛与脚本”的世界走向 “互联生态系统” 的世界。在这个新世界里,服务之间的连接与服务本身同样重要。
作为架构师,你的使命不再只是构建可靠的服务,而是构建 可靠的连接。胶水代码是可靠性的对立面。它是一块临时补丁,最终必然变成永久的负担。是时候停止建造脆弱的管道,开始构建 数字神经系统 了。
采用像 SchemaBridge 这样的持久编排引擎,你就能夺回工程的未来。你构建的系统能感知自身状态、能抵御故障,并对整个组织可见。这就是重拾速度、构建经久耐用系统的方式。
本文是我们“Building the Bridge”系列的一部分。另请参阅:为速度而设计:无模式事件摄取的理由——一种面向事件摄取的后期绑定方法。