Scala Akka Basics Cheat Sheet
Introduces Akka actors, actor systems and messaging with tell and ask, supervision, and the modern typed actor Behaviors API.
Defining an Actor
A classic Akka actor with a message-handling receive block.
import akka.actor.{Actor, ActorSystem, Props}class GreeterActor extends Actor { def receive: Receive = { case name: String => println(s"Hello, $name!") case _ => println("Unknown message") }}
Actor System & Messaging
Creating actors and sending messages with tell and ask.
val system = ActorSystem("greeter-system")val greeter = system.actorOf(Props[GreeterActor](), "greeter")greeter ! "World" // fire-and-forget (tell)// Ask pattern for a response (returns a Future)import akka.pattern.askimport akka.util.Timeoutimport scala.concurrent.duration._implicit val timeout: Timeout = Timeout(3.seconds)val future = greeter ? "World"system.terminate()
Core Akka Concepts
Key ideas behind the actor model in Akka.
- ActorRef- A lightweight, location-transparent handle used to send messages to an actor; never reference the actor instance directly
- Mailbox- Each actor has a queue of incoming messages processed one at a time, avoiding shared-state race conditions
- Supervision- Parent actors supervise children and decide how to handle failures: Restart, Resume, Stop, or Escalate
- tell (!) vs ask (?)- tell is fire-and-forget with no reply; ask returns a Future representing an eventual reply
- Props- Immutable configuration object describing how to create an actor instance, passed to actorOf
- context.become- Lets an actor swap its message-handling behavior at runtime for stateful protocols
Akka Typed Behaviors
The modern, type-safe actor API.
import akka.actor.typed.{ActorSystem, Behavior}import akka.actor.typed.scaladsl.Behaviorsobject Greeter { sealed trait Command final case class Greet(name: String) extends Command def apply(): Behavior[Command] = Behaviors.receiveMessage { case Greet(name) => println(s"Hello, $name!") Behaviors.same }}val system: ActorSystem[Greeter.Command] = ActorSystem(Greeter(), "greeter-system")system ! Greeter.Greet("World")
Stateful Typed Actors
Immutable state threaded through recursive Behaviors, with a typed ask that carries its own replyTo.
import akka.actor.typed.{ActorRef, Behavior}import akka.actor.typed.scaladsl.Behaviorsobject Counter { sealed trait Command final case class Increment(by: Int) extends Command final case class GetCount(replyTo: ActorRef[Int]) extends Command def apply(): Behavior[Command] = counting(0) private def counting(total: Int): Behavior[Command] = Behaviors.receiveMessage { case Increment(by) => counting(total + by) // new state via recursion, no var case GetCount(replyTo) => replyTo ! total Behaviors.same }}// Typed ask: the reply channel is part of the message, not implicit magicimport akka.actor.typed.scaladsl.AskPattern._import scala.concurrent.duration._implicit val timeout: akka.util.Timeout = 3.secondsval countF = counter.ask((replyTo: ActorRef[Int]) => Counter.GetCount(replyTo))
Custom Supervision Strategies
Per-exception restart policy in both the classic and typed actor APIs.
// Classic API: override supervisorStrategy on the parentimport akka.actor.{Actor, OneForOneStrategy, SupervisorStrategy}class Supervisor extends Actor { override val supervisorStrategy: SupervisorStrategy = OneForOneStrategy() { case _: ArithmeticException => SupervisorStrategy.Resume case _: NullPointerException => SupervisorStrategy.Restart case _: IllegalArgumentException => SupervisorStrategy.Stop case _: Exception => SupervisorStrategy.Escalate } def receive: Receive = { case _ => }}// Typed API: wrap a Behavior to declare a restart policy per exceptionimport akka.actor.typed.SupervisorStrategy.restartimport akka.actor.typed.scaladsl.Behaviorsval supervised: Behavior[Counter.Command] = Behaviors.supervise(Counter()).onFailure[IllegalStateException](restart)
Scheduling & Timers
Self-scheduled recurring messages with Behaviors.withTimers, and one-off scheduling via the classic scheduler.
import akka.actor.typed.scaladsl.Behaviorsimport scala.concurrent.duration._object Heartbeat { sealed trait Command private case object Tick extends Command def apply(): Behavior[Command] = Behaviors.withTimers { timers => timers.startTimerWithFixedDelay(Tick, 5.seconds) Behaviors.receiveMessage { case Tick => println("heartbeat") Behaviors.same } }}// Classic API: one-off delayed send via the ActorSystem's schedulersystem.scheduler.scheduleOnce(2.seconds) { greeter ! "delayed hello"}(system.dispatcher)
Stopping & Watching Actors
Graceful shutdown, confirmed termination, and reacting to another actor's death.
import akka.actor.{Actor, PoisonPill, Terminated}import akka.pattern.gracefulStopimport scala.concurrent.Awaitimport scala.concurrent.duration._// Fire-and-forget: processes any remaining mailbox messages, then stopsgreeter ! PoisonPill// Wait for confirmation the actor actually stopped, with a timeoutval stopped: Boolean = Await.result(gracefulStop(greeter, 5.seconds), 6.seconds)// DeathWatch: observe another actor's lifecycleclass Watcher(target: akka.actor.ActorRef) extends Actor { context.watch(target) def receive: Receive = { case Terminated(ref) => println(s"$ref stopped") }}
Advanced Akka Concepts
Production concerns beyond a single hand-rolled actor.
- Dispatcher- The thread-pool configuration that runs an actor's message processing; isolate blocking work onto its own dispatcher so it can't starve the default one
- Router- Distributes messages across a pool of routee actors (RoundRobinPool, BalancingPool) to parallelize work across cores
- EventSourcedBehavior (Akka Persistence)- Persists a stream of events instead of mutable state, replaying them on restart to rebuild the actor's current state
- Cluster Sharding- Distributes large numbers of stateful entity actors across cluster nodes, routing messages to the right node by entity ID
- BackoffSupervisor- Restarts a repeatedly failing child with exponentially increasing delay, preventing restart storms from overwhelming downstream resources
- TestKit / BehaviorTestKit- Synchronous and probe-based testing utilities for asserting on actor message flows without spinning up a real ActorSystem
- DeathWatch- context.watch/unwatch lets one actor observe another actor's termination via a delivered Terminated message
- Mailbox types- Default is unbounded FIFO, but BoundedMailbox and priority mailboxes are configurable per actor via dispatcher config for backpressure or ordering
Never call methods directly on an actor instance or share its mutable state outside the actor — always communicate through its ActorRef with messages, or you lose Akka's single-threaded-per-actor safety guarantee.