Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Exercises

Exercise 1: Simple Ping-Pong

import akka.actor._

class PingActor(pongActor: ActorRef) extends Actor {
  def receive: Receive = {
    case "Start" =>
      println("Ping")
      pongActor ! "Ping"
      
    case "Pong" =>
      println("Ping")
      Thread.sleep(500)  // Short delay
      pongActor ! "Ping"
  }
}

class PongActor extends Actor {
  def receive: Receive = {
    case "Ping" =>
      println("Pong")
      sender() ! "Pong"
  }
}

object PingPongApp extends App {
  val system = ActorSystem("PingPongSystem")
  
  val pongActor = system.actorOf(Props[PongActor], "pongActor")
  val pingActor = system.actorOf(Props(new PingActor(pongActor)), "pingActor")
  
  pingActor ! "Start"
}

Exercise 2: Counter Actor

import akka.actor._

class CounterActor extends Actor {
  private var counter = 0
  
  def receive: Receive = {
    case "Increment" =>
      counter += 1
      
    case "Decrement" =>
      counter -= 1
      
    case "Get" =>
      sender() ! counter
  }
}

object CounterApp extends App {
  val system = ActorSystem("CounterSystem")
  
  val counterActor = system.actorOf(Props[CounterActor], "counterActor")
  
  counterActor ! "Increment"
  counterActor ! "Increment"
  counterActor ! "Decrement"
  counterActor ! "Get"
  
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import scala.concurrent.ExecutionContext.Implicits.global
  
  implicit val timeout: Timeout = 5.seconds
  
  val future = counterActor ? "Get"
  
  future.map(result => println(s"Final counter value: $result"))
}

Exercise 3: Actor Hierarchy and Supervision

import akka.actor._

class ChildActor extends Actor {
  def receive: Receive = {
    case "DoWork" =>
      if (scala.util.Random.nextBoolean()) throw new RuntimeException("Failure!")
      println("Work done!")
  }
}

class ParentActor extends Actor {
  val childActor = context.actorOf(Props[ChildActor], "childActor")
  
  override val supervisorStrategy: SupervisorStrategy =
    OneForOneStrategy() {
      case _: RuntimeException => SupervisorStrategy.Restart
    }
  
  def receive: Receive = {
    case msg => childActor forward msg
  }
}

object SupervisionApp extends App {
  val system = ActorSystem("SupervisionSystem")
  
  val parentActor = system.actorOf(Props[ParentActor], "parentActor")
  
  parentActor ! "DoWork"
  parentActor ! "DoWork"
  parentActor ! "DoWork"
}

Exercise 4: Bank Account Actor

import akka.actor._

case class Deposit(amount: Double)
case class Withdraw(amount: Double)
case object GetBalance

class BankAccountActor extends Actor {
  private var balance = 0.0
  
  def receive: Receive = {
    case Deposit(amount) =>
      balance += amount
      
    case Withdraw(amount) =>
      if (balance >= amount) balance -= amount
      else println("Insufficient funds")
      
    case GetBalance =>
      sender() ! balance
  }
}

object BankAccountApp extends App {
  val system = ActorSystem("BankAccountSystem")
  
  val bankAccountActor = system.actorOf(Props[BankAccountActor], "bankAccountActor")
  
  bankAccountActor ! Deposit(100)
  bankAccountActor ! Withdraw(50)
  bankAccountActor ! GetBalance
  
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import scala.concurrent.ExecutionContext.Implicits.global
  
  implicit val timeout: Timeout = 5.seconds
  
  val future = bankAccountActor ? GetBalance
  
  future.map(balance => println(s"Final balance: $balance"))
}

Exercise 5: Master-Worker Pattern

import akka.actor._

case class Work(nums: List[Int])
case object GetSum

class WorkerActor extends Actor {
  def receive: Receive = {
    case Work(nums) =>
      val sum = nums.sum
      sender() ! sum
  }
}

class MasterActor(workerCount: Int) extends Actor {
  private var sum = 0
  private var receivedResponses = 0
  private val workers = (1 to workerCount).map(_ => context.actorOf(Props[WorkerActor]))
  
  def receive: Receive = {
    case Work(nums) =>
      val chunks = nums.grouped(nums.size / workerCount).toList
      chunks.zip(workers).foreach {
        case (chunk, worker) => worker ! Work(chunk)
      }
      
    case result: Int =>
      sum += result
      receivedResponses += 1
      if (receivedResponses == workerCount) context.parent ! sum
  }
}

object MasterWorkerApp extends App {
  val system = ActorSystem("MasterWorkerSystem")
  
  val masterActor = system.actorOf(Props(new MasterActor(3)), "masterActor")
  
  masterActor ! Work(List(1, 2, 3, 4, 5, 6, 7, 8, 9))
  
  masterActor ! GetSum
  
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import scala.concurrent.ExecutionContext.Implicits.global
  
  implicit val timeout: Timeout = 5.seconds
  
  val future = masterActor ? GetSum
  
  future.map(result => println(s"Final sum: $result"))
}