目录
- 一 监督机制
- 1.1 错误隔离性
- 1.2 系统冗余性
- 1.3 Actor的监督
- 1.3.1 监督体系
- 1.3.2 理解
- 1,3.3 监督策越
一 监督机制
1.1 错误隔离性
- 在学习Akka如何对失败情况进行响应之前,先了解一些在分布式应用程序中都应该遵循的通用策略:隔离错误。
- 假设每个组件都是一个定时炸弹,那么我们希望能够确保无论其中任何一个发生爆炸,都不会引发链式反应,导致其他组件也爆炸。
- 也可以说,我们希望能够隔离错误,或是将可能引发失败情况的组件分离开来。
1.2 系统冗余性
- broker通常用于在不停止系统的前提下添加新的服务节点或是关闭某个服务节点。
- 服务会连接到broker,从消息队列中获取并处理消息,JMS和RabbitMQ就是broker的例子。
1.3 Actor的监督
- Erlang将容错性引入了Actor模型,它使用的概念叫做监督(supervision)监督的核心思想就是把对于失败的响应和可能引起失败的组件分隔开,并且把可能发生错误的组件通过层级结构来组织,以便管理。
1.3.1 监督体系
- Akka使用Actor层级结构来描述监督,当我们创建Actor时,新建的Actor都是作为另一个Actor的子Actor,父Actor负责监督子Actor。
- Actor的路径结构就展示了它的层级结构,所以和文件系统中的文件夹有点像。
- 位于Actor层级结构顶部的是路径为/的根Actor。然后是路径为/user的守护Actor。
- 使用actorSystem.actorOf()函数创建的所有Actor都是守护Actor的子Actor(/user/yourActor)。
- 如果在一个Actor内部创建另一个Actor,那么可以通过调用context().actorOf使得新建的Actor成为原Actor的子Actor,这个新建的Actor就成为了Actor树结构中的下一个叶节点(/user/yourActor/newChild)。
- 根Actor下面还有其他Actor层级结构,系统操作相关的Actor在路径为/system的系统守护Actor下面。
- 路径/temp下面还包含了一个临时的Actor层级结构,用于完成Future的临时Actor就处于这个子树中。我们并不需要对这些细节担心过多,这些基本上都是Akka的内部实现,对于开发者是不可见的。
1.3.2 理解
我们将通过一个例子来帮助理解监督策略。假设寿司师很喜欢喝酒。他喝了酒以后经常给自己惹麻烦,所以此时他的经理必需要采取一些措施。经理可以选择一些不同的做法。
● 继续(resume):Actor继续处理下一条消息;
● 停止(stop):停止Actor,不再做任何操作;
● 重启(restart):新建一个Actor,代替原来的Actor;
● 向上反映(escalate):将异常信息传递给下一个监督者。
- 假设表示寿司师的Actor必需要制作寿司,他非常熟练,每做完一盘寿司,都要喝一杯酒来庆祝一下自己的杰作,服务员把客人点的菜单叠起来交给寿司师,寿司师就不断地制作寿司并喝酒,如果寿司师从早忙到晚的话,最后就无法处在最佳工作状态了(说得轻一点)。
- 如果寿司师犯了一个错误(比如说切到了自己的手指或是掉了一个盘子),那么这可能是可以接受的,监督者将告诉寿司师在这种情况下resume()。
- 一旦寿司师喝醉了,做了一盘巨丑无比的寿司,开始让一些客人不满了,那么经理(寿司师的监督者)就要负责对这种异常情况进行响应了,经理要说明寿司师喝醉了,并且把他辞退了,监督者请来另一个寿司师,接着根据下一个要做的菜单制作寿司,送走原来的寿司师,引进一个新寿司师的过程就相当于重启。
- 新来的寿司师比较年轻,忍不住也喝了起来。他彻底喝醉了,还打翻了一支蜡烛,餐厅着火了,经理处理不了这个错误——单单辞退喝醉的寿司师,再新请一个师傅可灭不了火!经理给他的监督者(餐厅老板)打电话,告诉他需要关门并且马上报警,这就是向上反映,此时经理的上级(饭店老板)就需要做决定。如果把异常向上反映给了守护Actor,那么就会关闭应用程序。
1,3.3 监督策越
Actor有默认的监督策略。如果没有修改监督策略,那么监督Actor的行为基本上和喝醉的寿司师例子中一样:
● Actor运行过程中抛出异常:restart();
● Actor运行过程中发生错误:escalate();
● Actor初始化过程中发生异常:stop()。
在默认监督策略中还定义了另一种情况:ActorKilledException,如果Actor被“杀”(kill)了,那么这个Actor的监督者会接收到一个ActorKilledException,执行stop()会接收到该异常。
@Override
public akka.actor.SupervisorStrategy supervisorStrategy() {
return new OneForOneStrategy(5, Duration.create("1 minute"),
akka.japi.pf.DeciderBuilder
// 打破盘子
.match(BrokenPlateException.class,
e -> SupervisorStrategy.resume())
// 喝醉了
.match(DrunkenFoolException.class,
e -> SupervisorStrategy.restart())
// 发生火灾
.match(RestaurantFireError.class,
e -> SupervisorStrategy.escalate())
// 需要停业,整顿
.match(TiredChefException.class,
e -> SupervisorStrategy.stop())
.matchAny(e -> SupervisorStrategy.escalate())
.build()
);
}
要注意的是,一般情况下使用默认的行为就可以了:如果Actor在运行中抛出异常,就重启Actor;如果发生错误,就向上反映或是关闭应用程序。不过如果Actor在构造函数中抛出异常,那么会导致ActorInitializationException,并最终导致Actor停止运行。因为在这种情况下应用程序不会继续运行,所以要对此特别注意。