Introduction to Akka – by Jan Van Sweevelt



Introduction to Akka – by Jan Van Sweevelt

1 0


akkaintro


On Github vansweej / akkaintro

Introduction to Akka

by Jan Van Sweevelt

The Challenge

  • Clock speed stopped growing since 2006
  • Moore's Law still applies but only the number of cores in a single chip is increasing

The Challenge (2)

Modern systems must be:

  • Fault tolerant
  • highly concurrent
  • truly scalable

Issue: Shared Mutable State

  • Multithreaded programs are hard to write and test
    • Non-deterministic
    • Race conditions
    • Locks are hard to use
The predominant approach to concurrency today is that of shared mutable state – a large number of stateful objects whose state can be changed by multiple parts of your application, each running in their own thread. Typically, the code is interspersed with read and write locks, to make sure that the state can only be changed in a controlled way and prevent multiple threads from mutating it simultaneously. At the same time, we are trying hard not to lock too big a block of code, as this can drastically slow down the application. More often than not, code like this has originally been written without having concurrency in mind at all – only to be made fit for a multi-threaded world once the need arose. While writing software without the need for concurrency like this leads to very straightforward code, adapting it to the needs of a concurrent world leads to code that is really, really difficult to read and understand. The core problem is that low-level synchronization constructs like locks and threads are very hard to reason about. As a consequence, it’s very hard to get it right: If you can’t easily reason about what’s going on, you can be sure that nasty bugs will ensue, from race conditions to deadlocks or just strange behaviour – maybe you’ll only notice after some months, long after your code has been deployed to your production servers.

We need a new high level programming model

  • easier to understand
  • deterministic
  • no shared/mutable state
  • fully utilize multi-core processors

Actors

  • Formalized in 1973 by Carl Hewitt
  • First commercial use by Ericsson, that invented Erlang
  • Akka is an Actor Model implementation in Scala

Actors (2)

  • Actors instead of Objects
  • No shared state between Actors
  • Asynchronous message passing
  • Location Transparancy
  • write highly performant concurrent code that is easy to reason about

Actors (3)

Actor uses

  • a thread
  • an object instance or component
  • a callback
  • a singleton
  • a service
  • a router, load-balancer
  • a Finite State Machine
  • lots of light-weight entities called actors
  • responsible for only a very small task, and is thus easy to reason about
  • communicate with messages

Anatomy of an Actor

Actor Code

import akka.actor.ActorSystem
import akka.actor.Actor
import akka.actor.Props

class myActor extends Actor {
  def receive = {
    case "Hello" => sender ! "world"

    case x: String => println("Hello, " + x)

    case _ =>
  }
}

val system = ActorSystem("mySystem")
val myActor = system.actorOf(Props[myActor], "myactor1")
not use new to create actor objects, we use the actorsystem and have an actorref returned

Send Messages

Tell: Fire and Forget

myActor ! "Jan"

Ask: Send and Receive Future

val future = myActor ? "Hello"

What about the Future

In the Scala Standard Library, a Future is a data structure used to retrieve the result of some concurrent operation. This result can be accessed synchronously (blocking) or asynchronously (non-blocking).

Synchronous Access

implicit val timeout = Timeout(5 seconds)
val future = myActor ? "Hello"
val result = Await.result(future, timeout.duration).asInstanceOf[String]

Asynchronous Access

With the map method

implicit val timeout = Timeout(5 seconds)
val future = myActor ? "Hello"
future.map {result => println(result)}

Asynchronous Access

With callbacks

implicit val timeout = Timeout(5 seconds)
val future = myActor ? "Hello"
future.onSuccess {
  case result: String => println(result)
}

onFailure and onComplete callbacks are provided too

Routers

Routers are actors that "routes" messages to other actors. Akka offers different routing strategies

  • Round Robin Router
  • Random Router
  • Smallest Mailbox Router
  • Broadcast Router

Routers

Creating a Router

val router = system.actorOf(Props[MyActor]
                   .withRouter(RoundRobinRouter(nrOfInstances = 5)))
router ! message

Supervision

  • Actors are organised in a hierarchical structure
  • Supervisors are regular actors that monitors other actors
  • When an actor creates another actor, it automatically becomes its supervisor
  • Monitored actors can be supervisors themselves too
  • Monitoring is based on "let it crash" concept (monitored actors throw an exception)
  • Supervisors define strategy how to handle the exceptions
Every actor has a parent and every actor can create children. The actor model is meant to help you achieve a high level of fault tolerance So how do we we deal with failure ActorSystem an actor ? no it is the guardian actor explain via system.actorOf from example

Supervision (2)

Two kind of Strategies

  • OneForOneStrategy: if one of the supervised children goes down, only that child is acted on
  • OneForAllStrategy: if one of the supervised children goes down, all supervised children are acted on

Supervision (3)

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

The Actor Lifecycle

  • preStart: Called when an actor is started, allowing you to do some initialization logic. The default implementation is empty.
  • postStop: Empty by default, allowing you to clean up resources. Called after stop has been called for the actor.
  • preRestart: Called right before a crashed actor is restarted. By default, it stops all children of that actor and then calls postStop to allow cleaning up of resources.
  • postRestart: Called immediately after an actor has been restarted. Simply calls preStart by default.

Some links

  • http://akka.io/
  • https://github.com/vansweej/akkaintro