首页 > 解决方案 > 如何重新启动被杀死的演员?

问题描述

我有一个主管和一个儿童演员,如下所示:

SupervisorActor.scala

import akka.actor.{Actor, ActorLogging, Props, Terminated}

object SupervisorActor {

  def props: Props = Props(new SupervisorActor)

  object KillFoo
  object TerminateFoo
  object RestartFoo

}

final class SupervisorActor extends Actor with ActorLogging {

  import com.sweetsoft.SupervisorActor._

  log.info(s"Start the actor ${self}")

  private val foo = context.actorOf(FooActor.props, "FOOCHILD")

  context.watch(foo)

  override def receive: Receive = {
    case KillFoo =>
      context.stop(foo)
    case Terminated(actor) =>
      log.info(s"Kill actor $actor")
    case TerminateFoo =>
      log.info("Terminate FooActor")
      foo ! new IllegalArgumentException
    case RestartFoo =>
  }
}

FooActor.scala

import akka.actor.SupervisorStrategy.{Escalate, Restart, Resume, Stop}
import akka.actor.{Actor, ActorLogging, OneForOneStrategy, Props}
import scala.concurrent.duration._

object FooActor {
  def props: Props = Props(new FooActor)
}

final class FooActor extends Actor with ActorLogging {

  log.info(s"Start the actor ${ self }")

  override val supervisorStrategy: OneForOneStrategy =
    OneForOneStrategy(maxNrOfRetries = 1, withinTimeRange = 1.minute) {
      case _: ArithmeticException => Resume
      case _: NullPointerException => Restart
      case _: IllegalArgumentException =>
        println("STOP exception raised.")
        Stop
      case _: Exception => Escalate
    }

  override def receive: Receive = {
    case _ => log.info("I got killed.")
  }
}

和 Main.scala

import akka.actor.ActorSystem
import com.sweetsoft.SupervisorActor.{TerminateFoo}

import scala.concurrent.Future
import scala.io.StdIn

object Main extends App {

  val system = ActorSystem("monarch")
  implicit val dispatcher = system.dispatcher

  try {
    // Create top level supervisor
    val supervisor = system.actorOf(SupervisorActor.props, "mupervisor")
    // Exit the system after ENTER is pressed
    Future {
     Thread.sleep(2000)
      supervisor ! TerminateFoo
    }

    StdIn.readLine()
  } finally {
    system.terminate()
  }

} 

被杀后FooActor,我想再次手动重新启动它,就像我做的那样:

private val foo = context.actorOf(FooActor.props, "FOOCHILD") 

怎么做?

我正在考虑创建一个函数,它将创建一个函数,FooActor并且在它被杀死后,只需调用该函数来启动一个新的FooActor.

标签: scalaakka

解决方案


代码存在一些问题。supervisorStrategy应该由SupervisorActor它负责监督,而不是儿童演员本身。

foo ! new IllegalArgumentException不会导致子actor终止,因为actor可以接受任何对象作为消息并且异常派生对象不会被特殊处理。它只会打印“我被杀了”。但否则忽略该消息并继续运行。特别是,它不会调用 supervisorStrategy 处理程序。

您可以:

  • 将预定义的系统PoisonPill消息发送给子actor以优雅地停止它(即在处理所有待处理消息之后)

  • 将预定义的系统Kill消息发送给子actor以在处理当前消息后立即停止它,并忽略其他排队的消息(如果有)。

  • TerminateFoo消息转发给它并在其处理程序中引发异常。在这种情况下,子对象的命运由 IllegalArgumentException 异常类型的 supervisorStrategy 处理程序决定(即在您的情况下为停止)。

.

override def receive: Receive = {
    case TerminateFoo =>
      log.info("Stopping FooActor by throwing an unhandled exception for supervisorStrategy to process it")
      throw new IllegalArgumentException
    case m => log.info(s"I received a message $m")
  }

有关详细信息,请参阅https://doc.akka.io/docs/akka/current/actors.html#stopping-actors


推荐阅读