创建一个监管策略

来源:互联网 发布:mmd模型制作软件 编辑:程序博客网 时间:2024/05/06 09:49

以下更加深入地讲解错误处理的机制和可选的方法。


为了演示我们假设有这样的策略:

import akka.actor.OneForOneStrategyimport akka.actor.SupervisorStrategy._import akka.util.duration._ override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {  case _: ArithmeticException      ⇒ Resume  case _: NullPointerException     ⇒ Restart  case _: IllegalArgumentException ⇒ Stop  case _: Exception                ⇒ Escalate}


我选择了一种非常著名的异常类型来演示监管和监控 中描述的错误处理方式的使用. 首先,它是一个一对一的策略,意思是每一个子actor会被单独处理(多对一的策略与之相似,唯一的差别在于任何决策都应用于监管者的所有 子actor,而不仅仅是出错的那一个). 这里我们对重启的频率作了限制,最多每分钟能进行 10 次重启; 所有这样的设置都可以被忽略,也就是说,相应的限制并不被采用, 留下了设置重启频率的绝对上限值或让重启无限进行的可能性。


构成主体的 match 语句的类型是 Decider, 它是 PartialFunction[Throwable, Directive]. 这一部分将 子actor的失败类型映射到相应的指令 .


缺省的监管机制
如果定义的监管机制没有覆盖抛出的异常,将使用上溯 机制.


如果某个actor没有定义监管机制,下列异常将被缺省地处理:


ActorInitializationException 将终止出错的子 actor
ActorKilledException 将终止出错的子 actor
Exception 将重启出错的子 actor
其它的 Throwable 将被上溯传给父actor
如果异常一直被上溯到根监管者,在那儿也会用上述缺省方式进行处理。


测试应用
以下部分展示了实际中不同的指令的效果,为此我们需要创建一个测试环境。首先我们需要一个合适的监管者:

import akka.actor.Actor class Supervisor extends Actor {  import akka.actor.OneForOneStrategy  import akka.actor.SupervisorStrategy._  import akka.util.duration._   override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {    case _: ArithmeticException      ⇒ Resume    case _: NullPointerException     ⇒ Restart    case _: IllegalArgumentException ⇒ Stop    case _: Exception                ⇒ Escalate  }   def receive = {    case p: Props ⇒ sender ! context.actorOf(p)  }}


该监管者将用来创建一个我们用来做试验的子actor:

import akka.actor.Actor class Child extends Actor {  var state = 0  def receive = {    case ex: Exception ⇒ throw ex    case x: Int        ⇒ state = x    case "get"         ⇒ sender ! state  }}


这个测试可以用 测试 Actor 系统 (Scala) 中的工具来进行简化, 比如 AkkaSpec 是 TestKit with WordSpec with MustMatchers 的混合


import akka.testkit.{ AkkaSpec, ImplicitSender, EventFilter }import akka.actor.{ ActorRef, Props, Terminated } class FaultHandlingDocSpec extends AkkaSpec with ImplicitSender {   "A supervisor" must {     "apply the chosen strategy for its child" in {      // 在此添加代码     }  }}


现在我们来创建 actor:


val supervisor = system.actorOf(Props[Supervisor], "supervisor")
 
supervisor ! Props[Child]
val child = expectMsgType[ActorRef] // 从TestKit的 testActor 中获取答案
第一个测试是为了演示 Resume 指令, 我们试着将actor设为非初始状态然后让它出错:


child ! 42 // 将状态设为 42
child ! "get"
expectMsg(42)
 
child ! new ArithmeticException // 让它崩溃
child ! "get"
expectMsg(42)
可以看到错误处理指令完后仍能得到42的值. 现在如果我们将错误换成更严重的 NullPointerException, 情况就不同了:


child ! new NullPointerException // 更严重的崩溃
child ! "get"
expectMsg(0)
而最后当致命的 IllegalArgumentException 发生时子actor将被其监管者终止:


watch(child) // 让 testActor 监视 “child”
child ! new IllegalArgumentException // 破坏它
expectMsg(Terminated(child))
child.isTerminated must be(true)
到目前为止监管者完全没有被子actor的错误所影响, 因为指令集确实处理了这些错误。而对于 Exception, 就不是这么回事了, 监管者会将失败上溯传递。


supervisor ! Props[Child] // 创建新的子actor
val child2 = expectMsgType[ActorRef]
 
watch(child2)
child2 ! "get" // 确认它还活着
expectMsg(0)
 
child2 ! new Exception("CRASH") // 上溯失败
expectMsg(Terminated(child2))
监管者自己是被 ActorSystem 的顶级actor所监管的。顶级actor的缺省策略是对所有的 Exception 情况 (注意 ActorInitializationException 和 ActorKilledException 是例外)进行重启. 由于缺省的重启指令会杀死所有的子actor,我们知道我们可怜的子actor最终无法从这个失败中幸免。


如果这不是我们希望的行为 (这取决于实际用例), 我们需要使用一个不同的监管者来覆盖这个行为。


class Supervisor2 extends Actor {  import akka.actor.OneForOneStrategy  import akka.actor.SupervisorStrategy._  import akka.util.duration._   override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {    case _: ArithmeticException      ⇒ Resume    case _: NullPointerException     ⇒ Restart    case _: IllegalArgumentException ⇒ Stop    case _: Exception                ⇒ Escalate  }   def receive = {    case p: Props ⇒ sender ! context.actorOf(p)  }  // 覆盖在重启时杀死所有子actor的缺省行为  override def preRestart(cause: Throwable, msg: Option[Any]) {}}


在这个父actor之下,子actor在上溯的重启中得以幸免,如以下最后的测试:
val supervisor2 = system.actorOf(Props[Supervisor2], "supervisor2") supervisor2 ! Props[Child]val child3 = expectMsgType[ActorRef] child3 ! 23child3 ! "get"expectMsg(23) child3 ! new Exception("CRASH")child3 ! "get"expectMsg(0)


0 0
原创粉丝点击