本章概述了监管背后的概念,并提供一些语义的说明。描述如何将其转换为实际代码的详细信息,请参阅Scala和Java API的相应章节。
示例项目:你可以看到在实践中如何使用akka-samples-supervision-java
监控手段
如Actor Systems监管中所述,描述了Actor之间的依赖关系:主管将任务委托给下属,因此必须回应他们的失败。当下属检测到故障(即抛出异常)时,它会暂停自身及其所有下属,并向其主管发送消息,指示故障。根据要监督的工作性质和失败的性质,主管可以选择以下四种选择:
- 恢复下属,保持其累积的内部状态重启下属
- 重启下属,清除其累积的内部状态
- 永久停止下属
- 升级失败,从而自身失败
重要的是始终将一个Actor视为监督层级的一部分,这解释了第四个选择的存在(作为一个主管也从属于另一个上级的主管)并且对前三个有影响:恢复一个Actor恢复它的全部下属,重新启动一个actor需要重新启动它的所有下属(但是请参阅下面的更多细节),类似地终止一个actor也将终止它的所有下属。需要注意的是,Actor类的preRestart的默认行为是在重新启动之前终止其所有子节点,但是可以重写此此方法;递归重启适用于执行此hook后留下的所有子节点。
每个主管配置有一个功能,将所有可能的故障原因(如:exceptions)转换为上面给出的四个选择之一;值得注意的是,此函数不会将失败的actor的身份作为输入。很容易想出结构的例子,这可能看起来不够灵活,例如:希望将不同的策略应用于不同的下属。在这一点上,理解监督是关于形成递归故障处理结构是至关重要的。如果你试图在一个层面做太多,那将很难推理,因此在这种情况下推荐的方法是增加一定程度的监督。
Akka实施了一种称为“parental supervisio(父母监督)”的特定形式。Actor只能由其他Actor创建 - 其中顶级Actor由library提供 - 每个创建的Actor都由其父级监督。这种限制使得Actor监督等级的形成是隐含的,并鼓励合理的设计决策。应该指出的是,这也保证了Actor不能从外部成为孤儿或附属于监督者,否则可能会让他们不知不觉。此外,这为actor应用程序的(子树)产生了一个自然而干净的关闭过程。
Actor系统将在其创建过程中启动至少三个Actor,如上图所示。有关actor路径的后果的更多信息,请参阅Actor路径的Top-Level Scopes for Actor Paths.。
/user: The Guardian Actor
可能与之交互最多的Actor是所有用户创建的演员的父母,监护人名为“/ user”。使用system.actorOf()创建的Actor是此actor的子级。这意味着当该监护人终止时,系统中的所有常用Actor也将被关闭。这也意味着这个监护人的监督策略决定了顶级Actor的监督方式。从Akka 2.1开始,可以使用设置akka.actor.guardian-supervisor-strategy来配置它,该策略采用SupervisorStrategyConfigurator的完全限定类名。当监护人升级失败时,根监护人的回应将是终止监护人,这实际上将关闭整个Actor系统。
/system: The System Guardian
这个特殊的监护人被引入,以实现有序的关闭序列,其中记录保持活动,而所有正常的Actor终止,即使记录本身是使用actor实现的。这是通过让系统监护人观察用户监护人并在接收到终止消息时启动其自己的关闭来实现的。顶级系统actor使用策略进行监督,该策略将在所有类型的Exception上无限期地重新启动,除了ActorInitializationException和ActorKilledException,它将终止有问题的子节点。所有其他throwables都会升级,这将关闭整个actor系统。
/: The Root Guardian
root guardian是所有所谓的“顶级”Actor的祖母,并使用SupervisorStrategy.stoppingStrategy监督路径的顶级范围中提到的所有特殊Actor,其目的是终止任何类型的孩子异常。由于每个真正的Actor都有一个主管,root guardian的主管不能成为真正的Actor。而且因为这意味着它“在泡沫之外”,它被称为“bubble-walker”。这是一个合成的ActorRef,它实际上会在遇到麻烦的第一个迹象时阻止其子进程,并在根守护者完全终止(所有子进程递归停止)后立即将actor系统的isTerminated状态设置为true。
重启意味着什么
当呈现在处理特定消息时失败的Actor时,失败的原因分为三类:
- 收到的特殊消息的系统(即编程)错误
- 处理消息期间使用的某些外部资源的(瞬态)故障
- Actor内部错误
除非失败是可识别的,否则不能排除第三个原因,这导致得出内部状态需要被清除的结论。如果主管决定其他孩子或其本身不受失败影响有意识地应用了错误内核模式 - 因此最好重启子进程。这是通过创建基础Actor类的新实例并用子项的ActorRef中的新实例替换失败的实例来实现的;执行此操作的能力是将actor封装在特殊引用中的原因之一。然后,新的actor继续处理其邮箱,这意味着重新启动在actor本身之外是不可见的,但是没有重新处理发生故障的消息。
重启期间的事件顺序如下:
- 暂停actor(这意味着它将不会处理正常的消息,直到恢复),并递归挂起所有子节点。
- 调用旧实例的preRestart hook(默认为向所有子节点发送终止请求并调用postStop)。
- 在preRestart期间等待所有被请求终止的子节点(使用context.stop())实际终止;这就像所有演员操作一样 - 是非阻塞的,来自最后一个被杀害的孩子的终止通知将影响到下一步的进展。
- 通过再次调用最初提供的工厂来创建新的actor实例。
- 在新实例上调用postRestart(默认情况下也调用preStart)。
- 向步骤3中未被杀死的所有子项发送重启请求;重新启动的子项将从步骤2递归地执行相同的过程。
- 恢复Actor。
生命周期监控意味着什么
与上述父项和子项之间的特殊关系相反,每个Actor都可以监视任何其他Actor。由于Actor从创作中完全活跃起来并且在受影响的主管之外看不到重启,唯一可用于监控的状态变化是从活着过渡到死亡。因此,监控用于将一个Actor与另一个Actor联系起来,以便它可以对另一个Actor的终止作出反应,这与对失败作出反应的监督相反。
生命周期监视是使用监视actor接收的Terminated消息实现的,其中默认行为是在没有另外处理的情况下抛出特殊的DeathPactException。要开始侦听Terminated消息,请调用ActorContext.watch(targetActorRef)。要停止侦听,请调用ActorContext.unwatch(targetActorRef)。一个重要的特性是无论监视请求和目标终止发生的顺序如何,都将传递消息,即,即使在注册时目标已经死亡,您仍然会收到消息。
如果主管不能重新启动其子女并且必须终止它们,监控特别有用,例如:如果在actor初始化期间出错。在这种情况下,它应该监视这些孩子并重新创建它们或安排自己稍后重试。
另一个常见的用例是,在没有外部资源的情况下,Actor需要失败,外部资源也可能是其自己的孩子之一。如果第三方通过system.stop(子)方法终止子项或发送PoisonPill,则主管可能会受到影响。
原文地址:https://doc.akka.io/docs/akka/2.5/general/supervision.html
来源:oschina
链接:https://my.oschina.net/u/2277632/blog/2963934