This blog introduces Akka examples and explore the ways in which it facilitates and simplifies the implementation of concurrent, distributed applications.
What is the Akka?
Akka is a toolkit and runtime for building highly concurrent, distributed, and fault-tolerant applications on the JVM. It is written in Scala, with language bindings provided for both Scala and Java.
All the complexity of creating and scheduling threads, receiving and dispatching messages, and handling race conditions and synchronization, is relegated to the framework to handle transparently.
Supervision
In an actor system, each actor is the supervisor of its children. If an actor fails to handle a message, it suspends itself and all of its children and sends a message, usually in the form of an exception, to its supervisor.
In Akka, supervisor strategies are the primary and straightforward mechanism for defining the fault tolerant behavior of your system.
When a failure occurs among the child actors the supervisor is informed and it has 4 options to handle this:
- Resume the child (and its children), keeping its internal state.
- Restart the child (and its children), clearing its internal state.
- Stop the child (and its children) permanently.
- Stop itself and escalate the error.
Remember that if the error is escalated and the super-supervisor decides to handle this by, say, a restart, then the supervisor, the failed actor, and all it’s sibling actors will be restarted.
Supervison strategies
Moreover, an Actor can decide to apply the action just to the failed children or to its siblings as well. There are two pre-defined strategies for this:
OneForOneStrategy
: Applies the specified action to the failed child only.AllForOneStrategy
: Applies the specified action to all of its children.
Creating a strategy
Begin by updating the GreetingsActor object by adding methods to create these two strategies
def one: SupervisorStrategy = {
OneForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 10 seconds) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: IllegalArgumentException => Stop
case _: Exception => Escalate
}
}
def all: SupervisorStrategy = {
AllForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 10 seconds) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: IllegalArgumentException => Stop
case _: Exception => Escalate
}
}
If no strategy is specified, the following default strategy is employed:
- If there was an error while initializing the actor or if the actor was killed, the actor is stopped.
- If there was any other kind of exception, the actor is simply restarted.
The Akka-supplied implementation of this default strategy is as follows:
final val defaultStrategy: SupervisorStrategy = {
def defaultDecider: Decider = {
case _: ActorInitializationException ⇒ Stop
case _: ActorKilledException ⇒ Stop
case _: Exception ⇒ Restart
}
OneForOneStrategy()(defaultDecider)
}
Akka allows for the implementation of custom supervisor strategies, but as the Akka documentation warns, do so with caution as incorrect implementations may lead to problems such as blocked actor systems (i.e. permanently suspended actors).
Conclusion
Akka, written in Scala, simplifies and facilitates the development of highly-concurrent, distributed, and fault tolerant applications, hiding much of the complexity from the developer. Doing Akka full justice would require much more than this single tutorial, but hopefully this introduction and its examples were sufficiently captivating to get you to want to read more.