Concepts, patterns and best practices for implementing actor systems using Akka
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
LICENSE.md
README.md

README.md

Real World Akka

This project aims to collect a set of recommendations and best practices for implementing actor systems using Akka. Whilst most examples may be in Scala, the concepts, practices and recommendations should apply to Java too.

General Programming Guidelines

Top Three

  1. Never block
  2. Never pass mutable state: don't close over fields, sender() or self references
  3. Encourage granular, hierarchical structure instead of enlarging top-level actors

"If In Doubt, Push It Out"

Instead of letting an actor's behaviour grow, delegate common behaviour or longer-running tasks to workers

The riskier the task, the further from the centre its actor should lie.

Actors can quickly grow in behaviour and responsibilities. This in turn can increase the risk of failure which can, and will, impact all children and clients. If you find yourself squinting whilst reasoning about the actor, consider pushing out common behaviour to an shortly-lived "worker". This worker is given sender() and all the other parameters it needs at creation time, and is then left to complete/wait/do whatever to achieve the task, stopping itself once complete. The added benefit is that you keep your actors small and encourage K.I.S.S through reduced responsibilities and behaviour that's testable and reusable. This is especially true for non-trivial domains.

Designing Actor Systems

Background reading and videos:

The One Rule

Test first. It's easy. Your interface is tell and you give it messages. It's that simple. Implement your actors through their tests, following the "Test. Code. Refactor." mantra. You will strugle to reason about your actors if you can't document the actor's input/output through tests and the larger system gets the harder it will be to reason.

The Error Kernel

Push higher-risk work to the leaf nodes, leaving state and longer living actors as parents

Patterns

Behavioural

Cameo

Anonymous Worker

Structural / Creational

Companion Object

In Scala, one can define a companion object that can be used as a factory for creation of instances of the class in question. In Akka this can be used for two purposes:

  1. Simplifying the creation of Props for the actor
  2. Grouping the messages (case classes) used for communicating with the actor: both input and output

Consider this actor:

class CounterActor(start: Int, initialSubscribers: List[ActorRef]) extends Actor {

	var current: Int = start

	def receive: Receive = normal(initialSubscribers)

	def normal(subs: List[ActorRef]): Receive = {
		case i: Int => from = from + i 
		case Freeze => context.become(frozen)
		case s@Subscribe(a)		=> context.become(normal(subs + a))
		case DistributeValue 	=> subscribers.foreach(who => who ! Value(from))
	}

	def frozen: Receive = {
		case Unfreeze => context.become(normal)
		// Ignore everything else
	}
}

CounterActor accepts 5 different message types: Int, Freeze, DistributeValue, Subscribe and Unfreeze, none of which are externally documented. Furthermore the creation of the Actor requires two inputs, which in most cases will be default starting values -- zero and empty respectively -- thus burdening the client/user. Instead, we could use a companion object to hide this creation detail and document the different message types:

object CounterActor {
	case object Freeze
	case object DistributeValue
	case object Unfreeze
	case class Subscribe(actor: ActorRef)

	def props(): Props = {
		Props(new CounterActor(0, List.empty[ActorRef]))
	}
}

So instead of:

context.actorOf(Props(new CounterActor(0, List.empty[ActorRef])))

you just write:

context.actorOf(CounterActor.props())

and not only save on the typing, but avoid having to scour receive blocks to determine the message types as they're clearly documented in the companion object. Of course this is even easier considering you've tested first...

Testing Actor Systems

Background reading and videos:

The One Rule

Yes, this bit's repeated. It's that important:

Test first. It's easy. Your interface is tell and you give it messages. It's that simple. Implement your actors through their tests, following the "Test. Code. Refactor." mantra. You will strugle to reason about your actors if you can't document the actor's input/output through tests and the larger system gets the harder it will be to reason.

Failures

Creative Commons License Real World Akka by Alex Collins is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Based on a work at http://github.com/atc-/RealWorldAkka.