创建一个监管策略


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


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

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
}


我选择了一种非常著名的异常类型来演示监管和监控 中描述的错误处理方式的使用. 首先,它是一个一对一的策略,意思是每一个子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 ! 23
child3 ! "get"
expectMsg(23)
 
child3 ! new Exception("CRASH")
child3 ! "get"
expectMsg(0)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值