理查德·库克 (Richard Cook) 博士在他现在著名的论文《 复杂系统如何失败》(How Complex Systems Fail) 中解释了复杂系统中失败的方式和原因:
复杂系统中的一些故障规则
4. 复杂系统包含不断变化的潜在故障组合。 这些系统的复杂性使得它们不可能在不存在多个缺陷的情况下运行。因为这些单独不足以导致故障,所以它们在操作期间被视为次要因素。
3. 灾难需要多次失败——单点失败是不够的。 当看似无害的小故障联合起来为系统性事故创造机会时,就会发生明显的灾难性故障。这些小故障中的每一个都是导致灾难的必要条件,但只有组合才足以允许失败。
14. 变化带来新的失败形式。 可靠系统中公开事故的低发生率可能会鼓励变革,尤其是新技术的使用,以减少低后果但高频率故障的数量。这些变化实际上可能为新的、低频率但后果严重的故障创造机会。因为这些新的、后果严重的事故发生率很低,所以在事故发生之前可能会发生多个系统变化,因此很难看出技术对故障的贡献。
这个网:复杂的系统本质上是不可避免的脆弱的。我们可以尝试,但我们不能阻止他们失败——有太多的活动部分、太多的变量和太多的组合需要理解和测试。即使是最小的变化或错误也可能引发灾难性的失败。
新希望
但是 多伦多大学关于复杂分布式系统中的灾难性故障的新研究 提供了一些希望——一种降低这些故障的风险和影响的潜在简单方法。
研究人员研究了经过广泛审查和测试但仍然以惊人的方式失败的分布式在线系统。
他们发现,大多数灾难性故障最初是由轻微的、非致命的错误引发的:配置错误、小错误、本应容忍的硬件故障。然后,按照上面的规则#3,必须发生一系列特定且不寻常的事件才能使灾难解开。
坏消息是这一系列事件无法提前预测或测试。
好消息是,复杂的分布式系统中的灾难性故障实际上可能比任何人以前想象的更容易修复。仔细观察,研究人员发现几乎所有 (92%) 灾难性故障都是对非致命错误处理不当的结果。错误处理中的这些错误导致系统行为不可预测,导致其他错误,这些错误并不总是被正确或可预测地处理,从而产生多米诺骨牌效应。
超过一半 (58%) 的灾难性故障可以通过仔细检查和测试错误处理代码来避免。在 35% 的情况下,错误处理代码中的错误微不足道:错误处理程序为空或仅记录失败,或者逻辑明显不完整。易于查找和修复的错误。如此简单,以至于研究人员构建了一个免费的 Java 字节码静态分析检查器 Aspirator 来捕获其中的许多问题。
在另外 23% 的情况下,非致命错误的错误处理逻辑非常错误,以至于基本的语句覆盖测试或仔细的代码审查都会发现这些错误。
研究人员遇到的下一个挑战是说服开发人员认真对待这些错误。他们必须引导开发人员理解为什么错误处理中的小错误、“实际上永远不会发生”的错误需要修复——以及为什么谨慎的错误处理如此重要。
这是我们所有人都需要接受的挑战——如果我们希望防止复杂分布式系统中的灾难性故障。