This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 13.6, “How to stop Akka Actors.”
Problem
You want to stop one or more Akka actors that are running in a Scala application.
Solution
There are several ways to stop Akka actors. The most common ways are to call system.stop(actorRef)
at the ActorSystem
level or context.stop(actorRef)
from inside an actor.
There are other ways to stop an actor:
- Send the actor a
PoisonPill
message - Program a
gracefulStop
To demonstrate these alternatives, at the ActorSystem
level you can stop an actor by using the ActorSystem
instance:
actorSystem.stop(anActor)
Within an actor, you can stop a child actor by using the context
reference:
context.stop(childActor)
An actor can also stop itself:
context.stop(self)
You can stop an actor by sending it a PoisonPill
message:
actor ! PoisonPill
The gracefulStop
is a little more complicated and involves the use of a Future
. See the Discussion for a complete example.
Discussion
Table 13-2 provides a summary of the methods that you can use to stop an actor.
Table 13-2. Ways to stop actors
Message | Description |
---|---|
! stop method |
The actor will continue to process its current message (if any), but no additional messages will be processed. See additional notes in the paragraphs that follow. |
PoisonPill message |
A PoisonPill message will stop an actor when the message is processed. A PoisonPill message is queued just like an ordinary message and will be handled after other messages queued ahead of it in its mailbox. |
gracefulStop method |
Lets you attempt to terminate actors gracefully, waiting for them to timeout. The documentation states that this is a good way to terminate actors in a specific order. |
As noted in Table 13-2, a major difference between calling the stop
method on an actor and sending it a PoisonPill
message is in how the actor is stopped. The stop
method lets the actor finish processing the current message in its mailbox (if any), and then stops it. The PoisonPill
message lets the actors process all messages that are in the mailbox ahead of it before stopping it.
Calling actorSystem.stop(actor)
and context.stop(actor)
are the most common ways to stop an actor. The following notes on this process are from the official Akka actor documentation:
- Termination of an actor is performed asynchronously; the
stop
method may return before the actor is actually stopped. - The actor will continue to process its current message, but no additional messages will be processed.
- An actor terminates in two steps. First, it suspends its mailbox and sends a
stop
message to all of its children. Then it processes termination messages from its children until they’re all gone, at which point it terminates itself. If one of the actors doesn’t respond (because it’s blocking, for instance), the process has to wait for that actor and may get stuck. - When additional messages aren’t processed, they’re sent to the
deadLetters
actor of theActorSystem
(though this can vary depending on the mailbox implementation). You can access these with thedeadLetters
method on anActorSystem
. - As shown in the following examples, the
postStop
life-cycle method is invoked when an actor is fully stopped, which lets you clean up resources, as needed.
The following subsections demonstrate examples of each of these approaches.
system.stop
and context.stop
This is a complete example that shows how to stop an actor by using the stop
method of an ActorSystem
:
package actortests import akka.actor._ class TestActor extends Actor { def receive = { case _ => println("a message was received") } } object SystemStopExample extends App { val actorSystem = ActorSystem("SystemStopExample") val actor = actorSystem.actorOf(Props[TestActor], name = "test") actor ! "hello" // stop our actor actorSystem.stop(actor) actorSystem.shutdown }
As mentioned, using context.stop(actorRef)
is similar to using actorSystem.stop(actorRef)
; just use context.stop(actorRef)
from within an actor. The context
variable is implicitly available inside an Actor
. This is demonstrated in Recipe 13.5, “Starting an Actor”.
PoisonPill message
You can also stop an actor by sending it a PoisonPill
message. This message will stop the actor when the message is processed. The message is queued in the mailbox like an ordinary message.
Here is a PoisonPill
example:
package actortests import akka.actor._ class TestActor extends Actor { def receive = { case s:String => println("Message Received: " + s) case _ => println("TestActor got an unknown message") } override def postStop { println("TestActor::postStop called") } } object PoisonPillTest extends App { val system = ActorSystem("PoisonPillTest") val actor = system.actorOf(Props[TestActor], name = "test") // a simple message actor ! "before PoisonPill" // the PoisonPill actor ! PoisonPill // these messages will not be processed actor ! "after PoisonPill" actor ! "hello?!" system.shutdown }
As shown in the comments, the second String
message sent to the actor won’t be received or processed by the actor because it will be in the mailbox after the PoisonPill
. The only output from running this program will be:
Message Received: before PoisonPill TestActor::postStop called gracefulStop
As its name implies, you can use the gracefulStop
approach if you want to wait for a period of time for the termination process to complete gracefully. The following code shows a complete example of the gracefulStop
approach:
package actortests.gracefulstop import akka.actor._ import akka.pattern.gracefulStop import scala.concurrent.{Await, ExecutionContext, Future} import scala.concurrent.duration._ import scala.language.postfixOps class TestActor extends Actor { def receive = { case _ => println("TestActor got message") } override def postStop { println("TestActor: postStop") } } object GracefulStopTest extends App { val system = ActorSystem("GracefulStopTest") val testActor = system.actorOf(Props[TestActor], name = "TestActor") // try to stop the actor gracefully try { val stopped: Future[Boolean] = gracefulStop(testActor, 2 seconds)(system) Await.result(stopped, 3 seconds) println("testActor was stopped") } catch { case e:Exception => e.printStackTrace } finally { system.shutdown } }
Per the Scaladoc, gracefulStop(actorRef, timeout)
“Returns a Future
that will be completed with success when existing messages of the target actor has [sic] been processed and the actor has been terminated.” If the actor isn’t terminated within the timeout, the Future
results in an ActorTimeoutException
. To keep this example simple, I use Await.result
, so the time period it waits for should be just slightly longer than the timeout value given to gracefulStop
.
If the order in which actors are terminated is important, using gracefulStop
can be a good way to attempt to terminate them in a desired order. The “Akka 2 Terminator” example referenced in the See Also section demonstrates a nice technique for killing child actors in a specific order using gracefulStop
and flatMap
.
“Killing” an actor
As you dig deeper into Akka actors, you’ll get into a concept called “supervisor strategies.” When you implement a supervisor strategy, you can send an actor a Kill
message, which can actually be used to restart the actor. The Akka documentation states that sending a Kill
message to an actor, “will restart the actor through regular supervisor semantics.”
With the default supervisory strategy, the Kill
message does what its name states, terminating the target actor. The following example shows the semantics for sending a Kill
message to an actor:
package actortests import akka.actor._ class Number5 extends Actor { def receive = { case _ => println("Number5 got a message") } override def preStart { println("Number5 is alive") } override def postStop { println("Number5::postStop called") } override def preRestart(reason: Throwable, message: Option[Any]) { println("Number5::preRestart called") } override def postRestart(reason: Throwable) { println("Number5::postRestart called") } } object KillTest extends App { val system = ActorSystem("KillTestSystem") val number5 = system.actorOf(Props[Number5], name = "Number5") number5 ! "hello" // send the Kill message number5 ! Kill system.shutdown }
Running this code results in the following output:
Number5 is alive Number5 got a message [ERROR] [16:57:02.220] [KillTestSystem-akka.actor.default-dispatcher-2] [akka://KillTestSystem/user/Number5] Kill (akka.actor.ActorKilledException) Number5::postStop called
This code demonstrates the Kill
message so you can see an example of it. In general, this approach is used to kill an actor to allow its supervisor to restart it. If you want to stop an actor, use one of the other approaches described in this recipe.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
See Also
- The “Akka 2 Terminator” example
- This Google Groups thread discusses how a
Kill
message is turned into an exception that is handled in the default supervision strategy so it doesn’t restart the actor. - The Akka actors documentation provides more examples of these approaches.
- The
gracefulStop
method is described on this Scaladoc page.