如何重启被杀的演员?

时间:2019-04-18 21:47:42

标签: scala akka

我有一个主管和一个儿童演员,其外观如下:

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

1 个答案:

答案 0 :(得分:4)

代码有一些问题。 supervisorStrategy应该放在SupervisorActor中,因为它负责监督,而不是儿童演员本身。

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

您可以:

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

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

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

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