Scala Akka Intro

Akka is a toolkit for building concurrent, distributed, and fault-tolerant systems on the JVM. It is built on the Actor Model — a programming model where independent units called actors communicate by sending messages to each other. Actors never share memory directly, which eliminates many concurrency bugs at the design level.

The Actor Model


Traditional concurrency:          Actor Model:
────────────────────────────────  ────────────────────────────────
Threads share memory              Actors have private state
Locks protect shared data         No shared data — no locks
Race conditions possible          Actors communicate via messages
Deadlocks possible                Messages processed one at a time
Hard to scale across machines     Actors can run on any machine

Actor diagram:

  Actor A                Actor B
  ┌─────────┐            ┌─────────┐
  │ State   │  message → │ State   │
  │ Behavior│            │ Behavior│
  └─────────┘            └─────────┘
       │                      │
       └── mailbox ───────────┘
           (message queue)

Adding Akka to Your Project

// build.sbt
libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor-typed" % "2.8.0",
  "ch.qos.logback" % "logback-classic" % "1.4.7"
)

Your First Actor (Typed Akka)

import akka.actor.typed._
import akka.actor.typed.scaladsl._

// Step 1: Define the messages an actor can receive
sealed trait GreeterMessage
case class Greet(name: String)       extends GreeterMessage
case class GreetMany(names: List[String]) extends GreeterMessage

// Step 2: Define the actor's behavior
object Greeter:
  def apply(): Behavior[GreeterMessage] =
    Behaviors.receiveMessage {
      case Greet(name) =>
        println(s"Hello, $name!")
        Behaviors.same   // stay in the same behavior

      case GreetMany(names) =>
        names.foreach(n => println(s"Hello, $n!"))
        Behaviors.same
    }

// Step 3: Create the actor system and send messages
@main def runGreeter(): Unit =
  val system = ActorSystem(Greeter(), "greeter-system")

  system ! Greet("Alice")
  system ! Greet("Bob")
  system ! GreetMany(List("Carol", "Dave", "Eve"))

  Thread.sleep(500)
  system.terminate()

Request-Reply Pattern

import akka.actor.typed._
import akka.actor.typed.scaladsl._

// Calculator actor
sealed trait CalcMsg
case class Add(a: Int, b: Int, replyTo: ActorRef[Int]) extends CalcMsg
case class Multiply(a: Int, b: Int, replyTo: ActorRef[Int]) extends CalcMsg

object Calculator:
  def apply(): Behavior[CalcMsg] =
    Behaviors.receiveMessage {
      case Add(a, b, replyTo) =>
        replyTo ! (a + b)
        Behaviors.same
      case Multiply(a, b, replyTo) =>
        replyTo ! (a * b)
        Behaviors.same
    }

// Client actor that sends requests and receives replies
object Client:
  def apply(calc: ActorRef[CalcMsg]): Behavior[Int] =
    Behaviors.setup { ctx =>
      calc ! Add(10, 5, ctx.self)
      calc ! Multiply(4, 7, ctx.self)

      Behaviors.receiveMessage { result =>
        println(s"Got result: $result")
        Behaviors.same
      }
    }

Client Actor                    Calculator Actor
────────────────                ────────────────────────────────
sends Add(10, 5, self)  ──────► receives message
                                computes 10 + 5 = 15
receives 15             ◄────── sends 15 to replyTo (Client)
prints "Got result: 15"

Actor Hierarchy and Supervision


ActorSystem (guardian)
    │
    ├── Worker Actor A
    │       │
    │       └── Child Actor A1
    │
    └── Worker Actor B

If a child actor crashes:
  → Parent receives failure signal
  → Parent decides: restart, stop, or escalate
  → System stays alive and resilient
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{Behavior, SupervisorStrategy}

object ResilientWorker:
  def apply(): Behavior[String] =
    Behaviors.supervise(
      Behaviors.receiveMessage { msg =>
        if msg == "crash" then throw new RuntimeException("Crashed!")
        println(s"Processing: $msg")
        Behaviors.same
      }
    ).onFailure[RuntimeException](SupervisorStrategy.restart)

Key Akka Concepts


Concept          Meaning
───────────────  ──────────────────────────────────────────
ActorSystem      Top-level container; creates actors; lifecycle manager
Actor            Independent unit with private state and a mailbox
Behavior         What the actor does when it receives a message
ActorRef         A reference (address) to send messages to an actor
!                Send a message (fire and forget, non-blocking)
?                Ask pattern — returns a Future with the reply
Mailbox          Queue of unprocessed messages (FIFO)
Supervision      Parent policy for handling child failures

When to Use Akka


Good use cases:
  ✓ Systems handling thousands of concurrent users
  ✓ Distributed systems across multiple machines
  ✓ Stateful background workers (session management, game state)
  ✓ Event-driven architectures
  ✓ Real-time data pipelines

Simpler alternatives for:
  ✗ Simple concurrent tasks → use Futures
  ✗ Batch processing → use parallel collections or Spark
  ✗ Single-threaded scripts → plain Scala

Akka Ecosystem


Akka Actors      → core concurrency and state management
Akka Streams     → reactive data pipeline processing
Akka HTTP        → HTTP server and client
Akka Cluster     → actors spread across multiple machines
Akka Persistence → actors that survive restarts (event sourcing)

Akka powers production systems at Twitter, LinkedIn, PayPal, and many other companies handling massive scale. Understanding the Actor Model fundamentals opens the door to building distributed systems that are resilient by design, not by accident.

Leave a Comment

Your email address will not be published. Required fields are marked *