Failure Models

我们一直都在提及 Failures 故障,但到目前为止他都是相当宽泛的通用概念,可能会包含许多的含义。类似我们对不同时间做出的假设,我们可以假设可能出现的几种不同类型的故障。Failure Model 故障模型就是在分布式系统中用来定义处理器会在什么情况下崩溃,然后算法则会在开发中来使用这些假设。比如我们可以假设处理器的崩溃是不可恢复的,或他只能够在某段时间之后才回复,或者他会在提供不正确的值时失去控制而产生故障。

在分布式系统中,处理器会依赖于其他的节点来执行算法,因此故障可能会导致整个系统出现不正确的执行状况。

我们会讨论分布式系统中的许多种故障模型,比如 Crash 崩溃,Omission 遗漏跟 Arbitray 任意的故障。这个列表并不完整,但他覆盖了大部分真实系统中适用的重要场景。

Crash Faults

正常情况下,我们期望处理器会正确的执行算法中的所有步骤。让处理器崩溃最简单的方式是停止算法所需执行的步骤并且也不发送消息给其他的处理器。换句话说,就是这个处理器崩溃了。大部分时间里,我们假设一个 Crash-Stop 崩溃停止的处理器抽象,他定义的是,一个处理器崩溃后,会一直保持在这个崩溃的状态。

这个模型没有假设处理器无法在崩溃后恢复,也不会试图去阻止他进行恢复。他只意味着这个算法并不依赖于不正确或不存活节点的恢复。没有东西会阻止处理器进行恢复、追赶上整个系统的状态,然后作为算法的下一个实例参与进去。

失效的处理器不能够继续在他们失效的轮次中再次作为协商的参与者加入。将这些恢复的处理器当成新的、具有不同标识符的参与者的模型并不等于 Crash-Recovery 模型 (后面会讨论),因为大部分的算法会使用预定义好的处理器列表,并定义能够容忍多少个故障的节点来明确其故障的语义。

Crash-Recovery 崩溃恢复是一个不同的处理抽象,他指的是处理器在执行算法必须的处理过程中被停止,并在稍后的某个时刻恢复并尝试继续执行后续的步骤。实现恢复的功能需要在系统中添加状态的持久化及定义恢复所需协议的支持。允许崩溃恢复的算法需要先定义所有可能进行恢复的状态,因为恢复的处理可能会需要从上次处理的步骤开始继续执行。

为了能够实现恢复的算法,需要将状态跟标识符同时列入考虑。在崩溃恢复这个场景中,还可以看到一个特殊的 Omission Failure 遗漏故障场景,在其他处理器的视角中,无法触达的跟已经崩溃然后恢复的处理器之间并没有区别。

Omission Faults

另一个故障的模型是 Omission Faults 遗漏故障,这个模型假设处理器会跳过或是没办法执行算法的某些步骤,或者是这些执行的结果对于其他的参与者来说是不可见的,或者他没办法从其他的参与者中接收或发送消息。遗漏故障能够感知处理器之间因为网络连接、路由器故障、网络堵塞等问题出现的网络分区。网络分区表现为独立的处理器或处理器组之间的消息遗漏。在这里可以使用遗漏某些处理器之间发送或接收的消息来模拟崩溃。

当一个处理器相对于其他的参与者来说运行的很慢,以至于无法在期望的时间内回复响应时,这是对于系统的其他部分来说,他可能已经被丢弃了。这个缓慢的节点不会直接停止,而是会尝试将已经不同步的结果发给其他的节点。

遗漏故障在算法在我们原本认为必须要执行的步骤被跳过或他的执行结果是不可见时就会产生。比如,当一个消息在返回给参与者时丢失,并且发送者在没有再次发送的情况下,当他已经成功送达了,继续执行后续的步骤。遗漏故障也可能会在断续的宕机、网络的超载、队列被填满等情形发生。

Arbitrary Faults

最难以攻克的故障类型是 Arbitrary 武断的或 Byzantine 拜占庭故障:一个处理器在违反了算法定义的情况下继续执行算法的步骤 (比如,如果一个处理器在一个一致性算法中选择了一个其他参与者没提议过的值)

这种故障可能是因为软件的 Bug 产生,或是因为处理器之间运行了算法的不同版本,这种场景的故障是比较容易发现跟理解的。在我们没办法控制所有的处理器时,这个问题会变得比较困难,因为其中的某些处理器可能会有意的去误导其他的处理器。

你可能听过听过航空领域中对拜占庭故障的容错能力:飞机跟航空系统并不会从子组件的表面值中获取响应,并对其进行交叉的校验。另一个广为人知的应用是加密的数字货币,他是没有权威的认证中心的,不同的部分控制这不同的节点,敌对的参与者会有丢弃某些值跟尝试通过提供错误响应来玩弄系统的动机。

Handing Failures

我们可以通过将处理器进行分组为算法引入冗余处理来掩盖故障:就算其中一个处理器发生失效了,用户也察觉不到这个故障。

在发生故障的时候也可能存在一些性能上的惩罚:正常的执行需要依赖于处理器能够正常响应,因此有些时候系统需要退回到较慢的执行路线来进行错误处理跟更正。许多的故障能够通过软件层级来避免,如代码审核、全面的测试、确保消息在指定的超时时间内送达跟进行重试、以及确保执行的步骤会跟本地执行的顺序一致。

我们要介绍的大部分算法都假设使用崩溃故障模型并通过引入冗余副本来处理故障。这些假设有助于创建运行的更好跟更易于理解跟实现的算法。