分布式系统的崩溃可能始于一个看似有限的错误,但当许多组件彼此依赖时,它会迅速演变成一连串故障。在 Sam Newman 的一次演讲中,这位独立软件工程顾问将土木工程中的渐进式坍塌(Progressive Collapse)概念与数字系统的韧性问题联系起来,并得出结论:不可能阻止所有错误,但可以降低错误扩散及其影响的可能性。
从 Ronan Point 大楼到云服务
Newman 回顾了 1968 年伦敦 Canning Town 地区 Ronan Point 大楼发生的局部坍塌事故。Mrs. Ivy Hodge 公寓内的一次有限燃气爆炸,导致一面承重建筑部分的外墙被掀落。爆炸位置上方的四层楼随之坍塌,随后又以连锁效应导致塔楼一角的一部分倒塌。事故造成四人死亡,而事故发生在 1968 年早上六点前,这一时间因素帮助减少了受害者人数。
对软件而言,其意义不在于建筑物与数字服务之间存在物理上的相似性,而在于故障模式相同:一个初始的小故障使某个组件失效,而其他组件又依赖于该组件,于是损害范围不断扩大。在分布式系统中,这种链式过程很难理解,因为服务、数据库、负载均衡器和 DNS 系统之间的关系,并不总是像一排多米诺骨牌那样清晰可见。
级联故障的数字案例
在 AWS 的us-east-1区域于 10 月发生的中断事件中,DynamoDB 的一个子组件在 AWS 架构内部更新 DNS 路由时出现问题。更新计划的一个缺陷导致 Route 53 中的路由被删除,随后依赖这些路由的服务开始发生故障,其中包括网络负载均衡器、计算服务、队列和 EKS。由于其他服务又使用这些基础服务,问题进一步扩散到面向用户的产品和公司,其中包括 Alexa、Ring、Slack、Snapchat、Zoom 和 Shopify;其中一些受到部分影响,另一些则完全受影响。
根据 Newman 对 AWS 报告的说明,该系统包含一个用于生成 DNS 变更计划的调度器,以及多个负责执行这些计划的执行器。其中一个计划耗时异常地长,随后又生成了另一个计划,并由不同的执行器快速执行。当旧计划后来完成执行时,它删除了新计划创建的路由。结果形成了一种竞争条件(Race Condition),导致 DNS 条目被删除。
第二个例子是一个销售二手汽车、摩托车和拖车的网站,该网站通过一个代号为 Sauron 的应用运行在十台服务器上。该应用通常处理 30 到 60 个并发请求,但当时遇到了超过 800 个请求。其中一个下游站点接受连接后便挂起且不再响应,而应用会等待 30 秒才终止请求。连接池因此耗尽,随后线程不断堆积,处理器将时间消耗在管理这些线程上,最终系统完全停止运行。用户反复点击刷新按钮又进一步增加了压力。
减少崩溃的三条路径
Newman 认为,专注于单一的“根本原因”会导致不恰当的简化。阻止火花出现可能有帮助,但并不能解决所有允许火势蔓延的条件。因此,韧性工程侧重于接受故障可能发生这一事实,并通过以下三个相互关联的类别,准备限制其影响:
- 降低风险:消除风险来源,或限制其耗尽资源的能力。在 AWS 案例中,这包括暂时停止自动 DNS 管理,直到问题得到解决,并为该组件增加更好的测试。在 Sauron 应用中,则可以使用负载丢弃(Load Shedding),只接受一定数量的请求,并拒绝超出系统能力的请求,而不是允许请求不断积累直至崩溃。
- 强化组件:通过冗余、监控、测试和改进执行方式,提高服务承受部分故障的能力。这可能意味着在负载均衡器后运行多个服务副本,但这一决策不能脱离组件的重要性和成本;当时 Sauron 应用已经处于退役阶段,且只占收入的一部分,因此运行额外的备用副本在商业上并不具有吸引力。
- 减少耦合:防止单个组件的故障传播到系统的其他部分。这包括使用隔舱(Bulkheads)、超时限制、替代路径,以及在可能的情况下采用本地优先的设计。系统某一部分对另一部分的依赖越少,就越有可能控制故障,而不是传播故障。
这对技术团队的实际工作意味着什么?
韧性并不意味着自动在两个区域或两个云平台运行系统。Newman 说明,相关选项包括备份与恢复、仅复制数据而不运行完整基础设施的 Pilot Light 架构、温备恢复,以及同时运行两个活跃站点。这些选项会逐步减少停机时间和数据丢失,但也会提高成本与复杂性。
他还警告说,站点之间的双向同步并不简单,尤其是在将其添加到一个原本没有为此设计的现有应用中时。多云也是如此:它可能减少对单一供应商的依赖,但要求针对每个平台具备不同的技能并进行不同的运营;仅仅将相同的代码部署到 AWS 和 Azure,并不能保证独立性,因为同一个软件缺陷可能会使两个环境同时中断。
结论
核心启示是,韧性并不是寻找一个永远不会故障的环境,而是设计系统,使其中一部分发生故障时,另一部分仍能继续运行。因此,应评估风险、容量边界、瓶颈、依赖路径,并在冗余、隔离和成本之间进行权衡。多区域或多云运行等决策,始终是与服务重要性相关的工程和商业选择,而不是适用于所有系统的通用配方。