Reputation: 34061
I have a supervisor and a child actor that looks as the following:
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.")
}
}
and 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()
}
}
After the FooActor
got killed, I would like to restart it manually again, like I did:
private val foo = context.actorOf(FooActor.props, "FOOCHILD")
How to do it?
I am thinking about to create a function, that is going to create the FooActor
and after it got killed, just call the function to start a new FooActor
.
Upvotes: 0
Views: 386
Reputation: 2928
There are a few problems with the code. supervisorStrategy
should be in the SupervisorActor
as it is responsible for supervision, not the child actor itself.
foo ! new IllegalArgumentException
won't cause the child actor to terminate as actors can accept any object as a message and Exception derived objects are not treated specially. It'll just print "I got killed." but otherwise ignore the message and keep running. In particular, it won't invoke supervisorStrategy
handler.
You can either:
send the pre-defined system PoisonPill
message to the child actor
to gracefully stop it (i.e. after processing all pending messages)
send the pre-defined system Kill
message to the child actor to stop
it immediately after processing the current message and ignore the
other queued messages if any.
TerminateFoo
message to it
and throw an exception in its handler. In this case the child
object's destiny is decided by supervisorStrategy handler for
IllegalArgumentException exception type (i.e. Stop in your case)..
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")
}
See https://doc.akka.io/docs/akka/current/actors.html#stopping-actors for details
Upvotes: 4