Akka:我怎样才能在另一个(非孩子)演员中捕捉到一个演员的失败?

时间:2017-07-20 22:08:36

标签: exception akka akka-supervision

我有两个演员:

ProcessManager 处理系统中的某些进程(例如,用户注册,购买等)

通知程序 - 如果ProcessManager中发生了某些错误,则应通知用户。 我需要捕获ProcessManager actor的失败(它失败并因任何原因而停止,例如,由于ActorInitializationException或达到最大重启时间并且Process manager actor已停止)。

   class ProcessManager extends Actor {
      override def receive: Receive = {
        ...
      }
    }

    class Notifier extends Actor {
      override def receive: Receive = {
        PROCESS MANAGER ACTOR FAILED AND STOPPED =>
          // Here I need to catch failure of ProcessManager actor
          // (it was failed and stopped for what ever
          // reason, for example, because of ActorInitializationException
          // or max restart time reached and Process manager actor was stopped).
          //
          // Then do some stuff, for example, send message to the client via web socket.
      }
    }


    class MyController @Inject() (cc: ControllerComponents, actorSystem: ActorSystem)
      (implicit exec: ExecutionContext) extends AbstractController(cc)  {


      // I need to catch failure of processManager in this actor.
      val notifier = actorSystem.actorOf(Props(classOf[Notifier]))

      def registerUser = Action.async {         

          // Actor may be stopped because of ActorInitializationException here
          val processManager = actorSystem.actorOf(Props(classOf[ProcessManager]))
              ...

         // OR it may be stopped here for any reason.
         processManager ! "some message which will fail and stop pm actor"

         Future.successfull(Ok("Thanks."))   
       }
   }

如何在通知程序演员中捕获 ProcessManager 演员的终止(因为失败)?

修改 让我解释一下我的问题的背景。

我正在Play控制器中创建PM actor并将消息发送给它(Tell),我立即向用户返回Ok响应。 PM actor创建另一个子actor,在创建过程中抛出ActorInitializationException。我需要通知用户(通过Web套接字,使用Notifier actor),出现问题。

1 个答案:

答案 0 :(得分:3)

Notifier演员永久停止时,您可以使用DeathWatch注册Terminated演员以接收ProcessManager讯息。 Notifier需要ProcessManager DeathWatch的{​​{1}} actor的引用,一种方法是将ProcessManager的引用作为消息发送(这是安全的,因为ActorRef是不可变的和可序列化的。)

class Notifier extends Actor {
  var processManager: Option[ActorRef] = None

  def receive: Receive = {
    case aRef: ActorRef =>
      if (processManager.isEmpty) {
        processManager = Some(aRef)
        context.watch(aRef) // register to "watch" the process manager
      }
    case Terminated =>
      // process manager was permanently stopped
    case ...
  }
}

object Demo extends App {
  val actorSystem = ActorSystem("my-actor-system")

  val notifier = actorSystem.actorOf(Props(classOf[Notifier]))
  val processManager = actorSystem.actorOf(Props(classOf[ProcessManager]))

  notifier ! processManager // send processManager's ref to the notifier
  ...
  processManager ! "some message which will fail and stop pm actor"
  ...
}

有一点需要注意:DeathWatch注册可能无法在尝试创建ActorInitializationException时引发ProcessManager之前发生。

如果您需要在Notifier的孩子抛出异常时向ProcessManager发送消息,请覆盖ProcessManager中的主管策略,并将此消息作为策略的一部分发送。类似的东西:

class ProcessManager extends Actor {
  import akka.actor.OneForOneStrategy
  import akka.actor.SupervisorStrategy._
  import scala.concurrent.duration._

  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
      case _: ActorInitializationException =>
        val notifier = context.actorSelection("/path/to/notifier")
        notifier ! CustomErrorMessage
        Stop
      case _: Exception => Escalate
   }

   def receive: Receive = {
     ...
   }
}