Outline

  • Part 1 - Creating actors and sending messages
  • Part 2 - Actor supervision and messaging
  • Part 3 - Futures and Routers
  • Part 4 - Akka HTTP

What is Akka?

Motivation

Outline

  • Part 1 - Creating actors and sending messages
  • Part 2 - Actor supervision and messaging
  • Part 3 - Futures and Routers
  • Part 4 - Akka HTTP

The Actor Model

  • Actors sends messages asynchronously
  • Actors process messages sequentially

Hello Actor

import akka.actor._

class GreetingActor extends Actor {
  def receive = {
    case message: String => println("Hello " + message)
  }
}

object GreetingActor extends App {
  val system = ActorSystem("MySystem")
  val greetingActor: ActorRef = system.actorOf(Props[GreetingActor])
  greetingActor ! "Hulk Hogan"
}
GreetingActor_1.java|kt

Sending Messages

import akka.actor._

case class SayHello(name: String)

class GreetingActor extends Actor {
  def receive = {
    case hello: SayHello => {
      println("Hello " + hello.name)
      sender ! hello.name
    }
  }
}

object GreetingActor extends App {
  val greetingActor = ActorSystem("MySystem").actorOf(Props[GreetingActor])
  greetingActor ! SayHello("Pope Benedict")
}
GreetingActor_2.java|kt

Scheduling Work

import akka.actor._
import scala.concurrent.duration._

object DoGreeting

class GreetingActor(delay: FiniteDuration) extends Actor {

  override def preStart() = {
    scheduleNextGreeting()
  }

  def receive = {
    case DoGreeting => {
      println("Hello!")
      scheduleNextGreeting()
    }
  }

  def scheduleNextGreeting() {
    import context.dispatcher
    context.system.scheduler.scheduleOnce(delay, self, DoGreeting)
  }
}
GreetingActor_3.java|kt

Part 1 - Exercises

Exercises - Running tests in Maven

  • Running multiple test classes

    $ mvn test -Dtest=workshop.part1.VettingActorTest,VettingSupervisorTest
  • Using wildcards

    $ mvn test -Dtest="workshop.part2.*"

Outline

  • Part 1 - Creating actors and sending messages
  • Part 2 - Actor supervision and messaging
  • Part 3 - Futures and Routers
  • Part 4 - Akka HTTP

Supervision through hierarchies

  • Actor supervision is recursive, enabling delegation of failure handling

Creating a supervisor

import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

class Supervisor extends Actor {

    override val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute, loggingEnabled = false) {
        case _: ArithmeticException      => Resume
        case _: NullPointerException     => Restart
        case _: IllegalArgumentException => Stop
        case _: Exception                => Escalate
      }

    def receive = {
      case p: Props => sender ! context.actorOf(p)
    }
}
SupervisorActor.java|kt

Using Death Watch

import akka.actor.{Props, Actor, Terminated, ActorRef, ActorSystem}

class DeathWatchActor extends Actor {

  override def preStart() {
    val greetingActor = context.actorOf(Props[VolatileGreetingActor])
    context.watch(greetingActor)
    greetingActor ! "print this message, please!"
  }

  def receive = {
    case Terminated(_) => println("looks like an actor has died :(")
  }
}
DeathWatchActor.java|kt

Life-cycle of Actors

class LifeCycleActor extends Actor {
  def receive = {
    case e: Exception => throw e
  }
  override def preStart() {
    println("preStart() - called on instance creation, but not on restart")
  }
  override def postStop() {
    println("postStop() - called on ANY actor-instance during shutdown")
  }
  override def preRestart(reason: Throwable, message: Option[Any]) {
    println("preRestart() - called on old instance")
  }
  override def postRestart(reason: Throwable) {
    println("postRestart() - called on new instance")
  }
}
LifeCycleActor.java|kt

What happens on an actor restart

Part 2 - Overview


Part 2 - Exercises

Outline

  • Part 1 - Creating actors and sending messages
  • Part 2 - Actor supervision and messaging
  • Part 3 - Futures and Routers
  • Part 4 - Akka HTTP

Future example

class ComputeActor extends Actor {
  def receive = { case s : String => sender ! s.length }
}

val system = ActorSystem()
val computeActor = system.actorOf(Props(new ComputeActor()))

val f1 = (computeActor ? "abc").mapTo[Int]
val f2 = (computeActor ? "bcd").mapTo[Int]

val futures: Future[List[Int]] = Future.sequence(List(f1, f2))

futures.map(numbers => numbers.sum)
  .recoverWith {
    case e: NullPointerException => Future { 0 }
  }
  .onComplete(result => println(result))

ComputeFuture.java|kt

Futures vs Actors

  • Futures suitable for simple request-response
  • Actors suitable for lasting operations
    • Mailbox, Life-cycle, supervision and death watch
  • Futures makes it easier to preserve ordering

Routers

  • Routers are actors specifically designed for concurrency
  • Have a group or pool of actors it can route messages to
  • Akka comes with different routers

Router example

class ComputeRouter_1 extends Actor {
    val router = context.actorOf(RoundRobinPool(50).props(Props[Routee]), "router1")

    def receive = {
        case s: String => router.tell(s, sender())
    }
}

class Routee extends Actor {
    def receive = { case s: String => sender ! s.length() }
}

object ComputeRouterExample extends App {
    val router: ActorRef = ActorSystem("MySystem").actorOf(Props[ComputeRouter_1])
    router ! "how long is this string?"
}
ComputeRouter_1.java|kt

Part 3 - Exercises

  • VettingFutureActor.java|kt and VettingFutureActorTest.java|kt
  • VettingRouter has no test! Can be tested by running with VettingRouterMain
  • VettingRouter should functionally behave the same as VettingFutureActor

Resilience - 99.9999999% uptime?

  • Eliminate shared state and let-it-crash philosophy
  • Akka and Erlang both implements actor model
  • Ericsson ADX301 switch 2M LoC with Nine-Nines uptime over 20yrs

Actor Model and Domain-Driven Design

Datainn - Gathering traffic data with Akka

Datainn - Where does Akka fit in?

Datainn - Actor Design

MultiScreen - Actor Design

Lessons learned with Akka

  • Keep actors small and simple
  • Actor hierarchies are easy to reason about
    • Design drawings actually match code base
  • Actor hierarchies are hard to understand
    • No synchronous methods call. Asynchronicity is harder.
  • High performance, but can be difficult to configure
  • Abstractions somewhat low-level
  • Actor supervision creates boiler plate code

Outline

  • Part 1 - Creating actors and sending messages
  • Part 2 - Actor supervision and messaging
  • Part 3 - Futures and Routers
  • Part 4 - Akka HTTP

Akka HTTP

  • Lightweight toolkit (not really a Framework)
  • Server- and client-side HTTP stack on top of akka-actor and akka-stream
  • The high-level, routing API
  • Low-level HTTPRequest/HTTPResponse API

Akka HTTP example

object WebServer {
  def main(args: Array[String]) {

    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()

    val route =
      path("hello") {
        get {
          complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
  }
}
AkkaHttpServer.java|kt

Part 4 - Exercises

  • Create a HTTP server front for the vetting of ads
  • Make HttpRoutesTest green

Thanks!



Questions?