How to stop Akka Actors (Scala)

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 the ActorSystem (though this can vary depending on the mailbox implementation). You can access these with the deadLetters method on an ActorSystem.
  • 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.

See Also