Single-Page

Akka with Scala

Prabhaker Mateti

pmateti@wright.edu

An introduction to Akka, assuming familiarity with the Actors and Scala
CEG 7370 Distributed Computing

Table of Contents

1 What is Akka?

  1. Akka is an implementation of Hewitt's Actor Model, in Scala (and hence JVM). It makes practical concessions to the pure actor model.
  2. "Akka is a toolkit and runtime for building highly concurrent, distributed, and fault tolerant event-driven applications on the JVM" http://www.akka.io. [Note the use of concurrent and distributed.]
  3. Helps with scaling both UP (utilizing multi-core processors) and OUT (utilizing the cloud/ grid/ cluster).
  4. http://www.typesafe.com/platform/getstarted "The best way to start learning Akka …"

1.1 What is Akka? v.Frivolous

  1. https://www.google.com/search?q=akka+mountain
  2. https://www.google.com/search?q=akka+finnish+goddess&safe=active
  3. https://www.google.com/search?q=akka+indian&safe=active
  4. https://www.google.com/search?q=akka+telugu&safe=active
  5. Should we give equal time to Bing, Yahoo, DuckDuckGo?

1.2 Who Is Using Akka?

fig-actor-users.png

1.3 What is Akka? v.nonFrivolous

  1. Includes an implementation of Actor Model, with improvements for practicality.
  2. Also includes Software Transactional Memory (STM).
  3. Event driven, middleware framework
  4. Part of Typesafe Stack. Open source, Apache License.
  5. Created by Jonas Bonér.

1.4 Akka Practical Use Overview

  1. Versions: Scala 2.11+, Akka 2.3.1+. Code for older versions may not work with these versions. Most, not 100%, is open source.
  2. The code shown in boxes below is not always self-contained enough to compile it stand-alone.
  3. scala.actor is deprecated. Caution: Many articles on the web are based on scala.actor. Use import akka.actor . http://docs.scala-lang.org/overviews/core/actors-migration-guide.html
  4. Akka library can be used in Java as well. http://www.pykka.org/ with Python.

1.5 How Does Akka work?

fig-actor-users.png

2 Actor Ops: Create, Send, Become, Supervise

case class Greeting (who: String)

class GreetingActor extends Actor {
  def receive = {
    case Greeting(who) =>
      sender ! (s"Hello, $who! Thanks.")
  }
}

2.1 Actor in Scala, contd

val acsy = ActorSystem("WSU-CEG-7370-Actors")
val grtr = acsy . actorOf(Props[GreetingActor], "greeter")
grtr ! Greeting("Brad Pitt")
  1. Props is a configuration class to specify options for the creation of actors.
  2. Try the above in scala REPL. You need the import shown below.
  3. import akka.actor.{ ActorRef, ActorSystem, Props, Actor }
  4. glitch: akka://WSU-CEG-7370-Actors/deadLetters

2.2 An Actor in Java

public class Greeting implements Serializable {
  public final String who;
  public Greeting(String who) {
    this.who = who;
  }
}
  1. Continued …

2.2.1 An Actor in Java, contd-1

public class GreetingActor extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  public void onReceive(Object message) throws Exception {
    if (message instanceof Greeting)
      getSender.tell("Hello " + ((Greeting) message).who + " Thanks.");
  }
}
  1. Continued …

2.2.2 An Actor in Java, contd-2

ActorSystem acsy = ActorSystem.create("WSU-CEG-7370-Actors");
ActorRef grtr = acsy . actorOf(
  new Props(GreetingActor.class), "greeter");
grtr.tell(new Greeting("Brad Pitt"));

2.3 Create an Actor

  1. Akka Actor receive message loop is exhaustive, different from Erlang and the late Scala Actors.
  2. Provide a pattern match for all messages that the actor can accept.
  3. To handle unknown messages have a default case case _ => ...
  4. Killing an Actor: victim ! Kill
  5. context.system.shutdown() the ActorSystem when everything’s finished

2.4 Actor System

  1. Akka Actors: Extremely lightweight (2.7 Million per GB RAM)
  2. Messages are the "kinetic energy" in an actor system.
  3. Actors can have lots of buffered "potential energy", but can't do anything with it until triggered by a message.
  4. Allocate (internal) threads: ActorSystem acsy = ActorSystem.create("WSU-CEG-7370-Actors");
  5. Everything is asynchronous and lockless
  6. Several actor systems, possibly with different configurations, can co-exist

2.5 Actor Lifecycle

fig-actor-lifecycle.png

2.6 Send

  1. The method named receive defines how messages are handled.
  2. ! aka tell.
  3. Asynchronous and Non-blocking – "Fire-forget"
  4. Order of send = order of arrival in the mailbox of receiving actor. Message ordering is guaranteed on a per-sender basis.
  5. The size of the mailbox can be bounded or unbounded.
  6. def ! (message: Any)
    (implicit sender: ActorRef = Actor.noSender)
  7. ? sends a message asynchronously and returns a Future representing a possible reply. Aka ask.

2.7 Become - Ex: HotSwapActor

import akka.actor._
class HotSwapActor extends Actor {
  import context._
  def angry: Receive = {
    case "ang" => sender ! "I am already angry >:("
    case "hap" => become(happy)
  }

  def happy: Receive = {
    case "hap" => sender ! "I am already happy :-)"
    case "ang" => become(angry)
  }

  def receive = {
    case "ang" => become(angry)
    case "hap" => become(happy)
  }
}

2.8 Become Overview

  1. Behaviors can be pushed and popped
  2. context.become Replaces the current behavior (i.e., the top of the behavior stack)
  3. Will now react according to the new behavior to the messages
  4. context.unbecome pop the stack

2.9 Become - Why?

  1. Implement finite state machines import akka.actor.FSM
  2. Spawn up an empty generic worker actor that can become whatever the master needs
  3. Let a highly contended actor adaptively transform into an actor pool or a Router
  4. Implement graceful degradation

2.10 Advanced become, Finite State Machines, and P3

  1. http://www.typesafe.com/activator/template/akka-sample-fsm-scala
  2. WSU CEG 7370 P3: Understand the example above, and answer the questions TBD.

2.11 Actor Hierarchy and Name Resolution

fig-actor-hierarchy.png

2.12 Become – Add to the Stack, Not Replace

import akka.event.Logging; import akka.actor._
case object Swap
class Swapper extends Actor {  import context._
  val log = Logging(system, this) 
  def receive = {
    case Swap =>
      log.info("Hi")
      become({
        case Swap =>
          log.info("Ho")
          unbecome() // just for fun
      }, discardOld = false) /* not replace */  }}

object SwapperApp extends App {
  val sys = ActorSystem("SwapperSystem")
  val a = sys.actorOf(Props[Swapper], name="swapper")
  // alternately logs Hi, Ho, ...
  a!Swap; a!Swap; a!Swap; a!Swap; a!Swap; a!Swap
}

2.13 Supervise

  1. Every single actor has a default supervisor strategy. Which is usually sufficient. But it can be overridden.
  2. When an actor crashes, its parent can either restart, stop it, or escalate the failure up the hierarchy of actors.
  3. A chain of responsibility, all the way to the top.
  4. "Let it crash!"
  5. Provides clean separation of business logic and error handling
  6. Traditional failure management:
    1. You are given a single thread of control
    2. Need to do all explicit error handling WITHIN this single thread
    3. Errors do not propagate between threads
    4. Leads to defensive programming, with error handling tangled with business logic

2.14 DeathWatch

  1. Register to receive Terminated message provided by the DeathWatch component of the actor system.
    import akka.actor.{ Actor, Props, Terminated }
    class WatchActor extends Actor {
      val child = context.actorOf(Props.empty, "child")
      context.watch(child)   // registration
      var lastSender = system.deadLetters
      def receive = {
        case "kill" =>
          context.stop(child); lastSender = sender()
        case Terminated(`child`) =>
          lastSender ! "finished"
      }
    }
    
  2. Note the back-quotes in Terminated(`child`)

3 Routers, Dispatchers, and Schedulers

  1. A router is a type of actor. Routes incoming messages to outbound actors.
  2. Akka download comes with several akka.routing strategies:
    1. RoundRobinRoutingLogic
    2. RandomRoutingLogic
    3. SmallestMailboxRoutingLogic
    4. BroadcastRoutingLogic
    5. ScatterGatherFirstCompletedRoutingLogic
    6. ConsistentHashingRoutingLogic

3.1 Execution Contexts

  1. Think of execution contexts as thread pools.
  2. ExecutionContext is similar to java.util.concurrent. Executor.
  3. import scala.concurrent.ExecutionContext
    
    

3.2 Dispatchers

  1. A dispatcher chooses an actor and a message from the actors mbox, and allocates a thread.
  2. Every ActorSystem will have a default dispatcher. Dispatchers implement the ExecutionContext interface.
  3. Each actor is configured to be run on a MessageDispatcher, and that dispatcher doubles as an ExecutionContext.
  4. Pinned dispatcher: Each actor has its own dedicated thread. Ideal for blocking operations.
  5. Calling Thread dispatcher: Provides a deterministic execution order

3.3 The Balancing Dispatcher

  1. Balancing dispatcher: Redistribute the work from busy actors to idle ones.
  2. All actors it handles share a mbox. A "work stealing" dispatcher.
  3. BalancingDispatcher is an actor. It is in-charge of sending messages to several actors (known as workers).

3.3.1 The Balancing Dispatcher

fig-dispatcher-balancing.png

3.4 Throttling

  1. Message Throttler: E.g., no more than 3 messages in 1 second.
  2. printer: A simple actor that prints whatever it receives
    class PrintActor extends Actor {
      def receive = { case x ⇒ println(x) }
    }
    val printer = system.actorOf(Props[PrintActor])
    
  3. TimerBasedThrottler
    val throttler = system.actorOf(
      Props(classOf[ TimerBasedThrottler ],
      3 msgsPer 1.second))
    throttler ! SetTarget(Some(printer))
    throttler ! "1"; throttler!"2"; throttler!"3"
    // These will wait for a second
    throttler ! "4"; throttler ! "5"
    

3.5 Circuit Breakers

  1. A circuit breaker provides stability and prevents cascading failures in distributed systems.
  2. import akka.pattern.CircuitBreaker, …
    class D extends Actor with ActorLogging {
      import context.dispatcher
    
      val breaker = new CircuitBreaker(
        context.system.scheduler,
        maxFailures = 5, callTimeout = 10.seconds,
        resetTimeout = 1.minute). onOpen(notifyMeOnOpen())
    
      def notifyMeOnOpen(): Unit = log.warning(
        "My CircuitBreaker is now open, and " + 
        "will not close for one minute")
    

3.6 Actor scheduling

  1. You can schedule sending of messages and execution of tasks (functions or Runnable).
  2. Schedule to send the "foo"-message to the testActor after 50ms:
    system.scheduler().scheduleOnce(
      Duration.create(50, TimeUnit.MILLISECONDS),
      testActor, "foo", system.dispatcher(), null   )
    

4 Futures and Promises

  1. Akka gets Futures and Promises from Scala.
  2. A Future is a placeholder object for a result that has not been computed yet. A related CS term is lazy evaluation.
  3. A Future may only be assigned once. Needs an ExecutionContext
  4. Invoke the future method which starts an asynchronous computation and returns a future. The result becomes available once the future completes.
    import scala.concurrent.{ future, promise }
    import scala.concurrent.ExecutionContext.Implicits.global
    val s = socialNetwork.createSessionFor("user", credentials)
    val f: Future[List[Friend]] = future {
      s.getFriends()           // may take a while
    }
    

4.1 Promises

  1. A Promise is a writable, single-assignment container that completes a future. Methods: success, failure, complete
    import scala.concurrent.{ future, promise }
    import scala.concurrent.ExecutionContext.Implicits.global
    val p = promise[T]
    val f = p.future
    val prdcr = future {               // producer
      val r = produceSomething()
      p . success . r
      continueDoingSomethingUnrelated()
    }
    val cnsmr = future {              // consumer
      startDoingSomething()
      f . onSuccess {
        case r => doSomethingWithResult()
      }}
    

5 Distributed Actor System

fig-actor-path.png

5.1 Actor References

  1. Actor references may be obtained by (i) creating actors or (ii) looking them up
  2. Typically, we create actors beneath the guardian actor using the ActorSystem.actorOf method and then …
  3. Spawn the actor tree using ActorContext.actorOf.

5.1.1 Looking up Actors by Concrete Path

  1. ActorSystem.actorSelection method
  2. Send a message, such as the built-in Identify message, to the actor and use the sender reference of a reply from the actor.
  3. actorFor is deprecated in favor of actorSelection because actor references acquired with actorFor behave differently for local and remote actors. Beware: actorOf vs. actorSelection vs. actorFor
  4. Matching on paths similar to shells «*» and «?»: context.actorSelection("../*") ! msg

5.1.2 Absolute vs. Relative Paths

  1. Send a message to a specific sibling:
  2. context . actorSelection ("../brother") ! msg
  3. Absolute paths: context.actorSelection("/user/serviceA") ! msg

5.2 Remote Actors and Akka Clusters

  1. Remote Actors ActorSystem configuration
fig-actor-remote-1.png

5.3 Clusters

  1. Automatic cluster-wide deployment
  2. Decentralized P2P gossip-based cluster membership
  3. Leader “election”
  4. Adaptive load-balancing (based on runtime metrics)
  5. Automatic replication with automatic fail-over upon node crash
  6. Automatic adaptive cluster rebalancing
  7. Highly available configuration service

5.4 Enable Clustering

akka {
  actor {
    provider = "akka.cluster.ClusterActorRefProvider"
    ...
  }

  extensions = ["akka.cluster.Cluster"]

  cluster {
    seed-nodes = [
      "akka://ClusterSystem@127.0.0.1:2551",
      "akka://ClusterSystem@127.0.0.1:2552"
    ]
    auto-down = on
  }
}

5.5 Remote Deployment

fig-actor-remote-deployment.png
  1. http://doc.akka.io/docs/akka/snapshot/general/addressing.html

6 Termination

What Does "Finished" Mean? The most natural answer to this question appears to be, "When all the Mailboxes are empty." Natural, yes; correct, no. :)

fig-terminated-q.png

6.1 The Reaper

fig-the-reaper.png

6.2 Reaper Source Code

import akka.actor.{Actor, ActorRef, Terminated}
import scala.collection.mutable.ArrayBuffer

object Reaper { case class WatchMe(ref: ActorRef); }

abstract class Reaper extends Actor {
  import Reaper._
  val watched = ArrayBuffer.empty[ActorRef]

  /* Derivations must implement this method. Called 
   * when everything is dead */

  def allSoulsReaped(): Unit

  final def receive = {
    case WatchMe(ref) =>
      context.watch(ref)
      watched += ref
    case Terminated(ref) =>
      watched -= ref
      if (watched.isEmpty) allSoulsReaped()
  }
}

6.3 PoisonPill

fig-akka-poison-pill.png

7 Large Scale Examples of Akka + Scala

  1. http://www.playframework.com/ "The High Velocity Web Framework For Java and Scala" Built on Akka, Play provides predictable and minimal resource consumption (CPU, memory, threads) for highly-scalable applications. RESTful by default.
    1. REST stands for "Representational State Transfer". It describes a web architecture.
  2. Play is used in http://www.typesafe.com/platform/getstarted
  3. http://spray.io/ "Elegant, high-performance HTTP for your Akka Actors." spray is an open-source toolkit for building REST/HTTP-based integration layers on top of Scala and Akka. Being asynchronous, actor-based, fast, lightweight, modular and testable it's a great way to connect your Scala applications to the world.
  4. http://twitter.github.io/finagle Finagle is an extensible RPC system for the JVM. Finagle is written in Scala, but provides both Scala and Java APIs.

8 TypeSafe Activator: Akka Get Started

  1. Reactive Applications: Concurrency + events
  2. Play Framework
  3. Akka Runtime
  4. Scala Programming Language
  5. Activator Reactive Developer Environment
  6. http://www.typesafe.com/platform/getstarted "The best way to start learning Akka …"

8.1 Activator Practical Details

  1. ssh to Laptop/OSIS Lab machine 130.108.17.112
  2. % /usr/local/activator-1.1.1/activator ui -Dhttp.address=130.108.17.112 -Dhttp.port=8080 -Dbrowser=/usr/bin/google-chrome
  3. Copies/Creates its applications in ~/.activator/ and ~

8.2 Akka, without Activator, Practical Details

  1. Akka can be used without defining a configuration, since sensible default values are provided.
    // application.conf at the root of the class path.
    akka {
      loggers = ["akka.event.slf4j.Slf4jLogger"]
      loglevel = "DEBUG"
      stdout-loglevel = "WARNING"
      actor {
        provider = "akka.cluster.ClusterActorRefProvider"
        default-dispatcher {
          # set to 1 for as fair as possible
          throughput = 10
        }
      }
      remote {    netty.tcp.port = 2552  }
    }
    

9 References

  1. http://akka.io/docs/akka/snapshot/AkkaScala.pdf about 360 pages. Nearly all the code snippets and figures are from here. Reference.
  2. Jonas Biner, "Above the Clouds: Introducing Akka", 2011. Web search for "Above the Clouds: Introducing Akka". Video or pdf. Highly recommended.