1.3 容错

我们刚刚在例子中涉及到如何快速且简单地实现容错;现在让我们再深入一点。 (容错在第三章中详细讨论) 卖票的例子提到,消息传递的方法允许系统继续工作,即使其中一部分永远等待,不能工作。 其中的一个原因是隔离,actors 不直接相互对话。 actor 从来不会阻塞,或永远等待,因为它给另一个 actor 发消息。 因为消息发送到信箱,发送者立即可以做其他事情。 也许消息永远不会到达期望的 actor,或者从不会被处理,但是发送的 actor 至少不会因为发送消息到一个地址而崩溃。 这对对象和调用方法不成立。 一旦你调用了一个方法,你就陷进去了。 图 1.7 展示了 actor 和对象在这方面的差别。

Figure 1.7 消息传递 vs 函数调用

隔离提供了另一个机会。 如果一个 actor 崩溃,被另一个 actor 代替,新的 actor 可以通过原来地址联系到, 会发生什么?发送到这个地址的消息会被新 actor 接收,只要崩溃的 actor 被正确替换。 错误被限制在出错的 actor 中,之后的消息可以被新 actor 处理。 (为了能正确处理,出错的 actor 不应该再继续处理消息。 它应该崩溃,然后被烧掉,不应该再次出现。) 像 Spring 之类的框架在容器/bean 层级处理,Akka 把可替换性引入运行时 (这就是 Actor“让它崩溃”的概念):我们预计到会出现单个处理者不能执行任务的情况, 我们准备着重新分配工作以避免灾难性的失败。

这里要注意:这是双向的。 即使失败在链中是向上的,下面的 actor 也可以继续直到新的监督者到来。 没有破坏的链,最重要的是,找出链可能破坏的所有可能方式,并为每种情形进行特殊处理, 不再是开发者的责任(在共享可变状态模型中,异常是唯一的工具,而且如下面会看到的, 它们与当前的虚拟机绑定,向上和向下影响调用栈)。 崩溃的 Actor (本例中是打印室)可以被替换掉,甚至不打断其他合作者,如图 1.8 所示:

Figure 1.8 替换掉崩溃的打印室

信箱中的被崩溃了的打印室处理的消息可以传递给新的打印室(当确定还没有被处理)。 代理不会注意到任何问题,系统继续售票。 这个情景是 Akka 提供的容错策略的一个例子,成为重启策略。 其他可用策略包括:恢复,停止,升级。 这些策略在第四章中详细解释。 Akka 提供了为 Actors 中发生的特定异常选择策略的方法。 因为 Akka 控制着 Actors 之间的消息如何处理,知道 Actors 的地址, 它可以终止抛异常的 Actor 的消息传递,检查特定异常应使用何种策略,并采取相应动作。 (这有点像《铁窗喋血》中的世界:唯一的失败,是通信失败)

容错不表示每个可能的错误都会被捕获,然后完全恢复。 容错系统是这样的系统:可以限制和隔离系统中特定部分的错误,避免系统崩溃。 目标是令系统继续运行,这通过重启打印室达到。 不同的失败需要相应地错略。 一些失败可以通过重启系统中的某部分,其他错误可能不能在监测点解决,作为更大子系统的一部分, 需要在上层处理,

正如你可能会预料的,在共享可变状态方法中替换工作有问题的对象几乎是不可能的, 除非你打算建立自己的框架来支持它。 而且这不限于工作有问题的对象,如果你打算替换一个特定对象的行为,该怎么办呢? (如我们之后看到的,Akka 也提供了对 Actor 进行热交换的功能) 因为你不能控制方法如何被调用,也不能挂起一个方法,重定向到另一个新对象, 消息传递方法提供的灵活性是无可匹敌的。

不深入细节,我们来简单看看异常处理。 标准的、非并发的异常在调用层次中向上抛。 对象需要处理这个异常,或者重新抛出。 无论何时异常发生,你需要停止正在处理的常规业务,转到错误处理,处理完后,立即从离开的地方继续进行。 因为这很难,大部分开发者喜欢把错误顺着栈一路向上抛出,留给某些框架来处理, 框架会在出现错误迹象的时候终止进程。 在 Java 中,存在两种异常(检查过的和运行时的)令情形更加阴暗, 对每种异常的适用情况的疑惑使得更可能在遇到异常时回避,希望其他人来处理。

以上讨论的还只是非并发时候的异常处理。 现实中异常几乎不能在线程间共享,除非你打算建立大量的基础设施来处理。 异常不能自动传出线程所在的线程组,这意味着你得用其他方式在不同线程组的线程之间交换异常。 大多数情况下,如果线程遇到某些异常,可以选择忽略错误,继续运行,或者完全停止执行, 这种是最简单的方案。 你能在日志中找出线程崩溃或停止的迹象,但是向系统其他部分传递这些信息没那么简单。 重启线程,而且提供正常工作需要的状态,对于手动来做非常难。 当线程分布在多台机器上时,这又难了一个数量级。 Akka 提供了处理错误的模型,无论 actors 在一台机器上,还是扩展到多台,如第三章中所示。 你能在对核心问题处理方法的对比中看出, Akka 不仅可以解决并发问题,在某些情况下,容错方面的优势也有很大的吸引力。