Jay Taylor's notes

back to listing index

Actors — Akka Documentation

[web search]
Original source (doc.akka.io)
Tags: scala programming docs concurrency actors doc.akka.io
Clipped on: 2014-04-14

The Actor Model provides a higher level of abstraction for writing concurrent and distributed systems. It alleviates the developer from having to deal with explicit locking and thread management, making it easier to write correct concurrent and parallel systems. Actors were defined in the 1973 paper by Carl Hewitt but have been popularized by the Erlang language, and used for example at Ericsson with great success to build highly concurrent and reliable telecom systems.

The API of Akka’s Actors is similar to Scala Actors which has borrowed some of its syntax from Erlang.

Creating Actors

Note

Since Akka enforces parental supervision every actor is supervised and (potentially) the supervisor of its children, it is advisable that you familiarize yourself with Actor Systems and Supervision and Monitoring and it may also help to read Actor References, Paths and Addresses.

Defining an Actor class

Actor classes are implemented by extending the Actor class and implementing the receive method. The receive method should define a series of case statements (which has the type PartialFunction[Any, Unit]) that defines which messages your Actor can handle, using standard Scala pattern matching, along with the implementation of how the messages should be processed.

Here is an example:

  1. import akka.actor.Actor
  2. import akka.actor.Props
  3. import akka.event.Logging
  4.  
  5. class MyActor extends Actor {
  6. val log = Logging(context.system, this)
  7. def receive = {
  8. case "test" => log.info("received test")
  9. case _ => log.info("received unknown message")
  10. }
  11. }

Please note that the Akka Actor receive message loop is exhaustive, which is different compared to Erlang and the late Scala Actors. This means that you need to provide a pattern match for all messages that it can accept and if you want to be able to handle unknown messages then you need to have a default case as in the example above. Otherwise an akka.actor.UnhandledMessage(message, sender, recipient) will be published to the ActorSystem's EventStream.

Note further that the return type of the behavior defined above is Unit; if the actor shall reply to the received message then this must be done explicitly as explained below.

The result of the receive method is a partial function object, which is stored within the actor as its “initial behavior”, see Become/Unbecome for further information on changing the behavior of an actor after its construction.

Props

Props is a configuration class to specify options for the creation of actors, think of it as an immutable and thus freely shareable recipe for creating an actor including associated deployment information (e.g. which dispatcher to use, see more below). Here are some examples of how to create a Props instance.

  1. import akka.actor.Props
  2.  
  3. val props1 = Props[MyActor]
  4. val props2 = Props(new ActorWithArgs("arg")) // careful, see below
  5. val props3 = Props(classOf[ActorWithArgs], "arg")

The second variant shows how to pass constructor arguments to the Actor being created, but it should only be used outside of actors as explained below.

The last line shows a possibility to pass constructor arguments regardless of the context it is being used in. The presence of a matching constructor is verified during construction of the Props object, resulting in an IllegalArgumentEception if no or multiple matching constructors are found.

Dangerous Variants

  1. // NOT RECOMMENDED within another actor:
  2. // encourages to close over enclosing class
  3. val props7 = Props(new MyActor)

This method is not recommended to be used within another actor because it encourages to close over the enclosing scope, resulting in non-serializable Props and possibly race conditions (breaking the actor encapsulation). We will provide a macro-based solution in a future release which allows similar syntax without the headaches, at which point this variant will be properly deprecated. On the other hand using this variant in a Props factory in the actor’s companion object as documented under “Recommended Practices” below is completely fine.

There were two use-cases for these methods: passing constructor arguments to the actor—which is solved by the newly introduced Props.apply(clazz, args) method above or the recommended practice below—and creating actors “on the spot” as anonymous classes. The latter should be solved by making these actors named classes instead (if they are not declared within a top-level object then the enclosing instance’s this reference needs to be passed as the first argument).

Warning

Declaring one actor within another is very dangerous and breaks actor encapsulation. Never pass an actor’s this reference into Props!

Recommended Practices

It is a good idea to provide factory methods on the companion object of each Actor which help keeping the creation of suitable Props as close to the actor definition as possible. This also avoids the pitfalls associated with using the Props.apply(...) method which takes a by-name argument, since within a companion object the given code block will not retain a reference to its enclosing scope:

  1. object DemoActor {
  2. /**
  3. * Create Props for an actor of this type.
  4. * @param magciNumber The magic number to be passed to this actor’s constructor.
  5. * @return a Props for creating this actor, which can then be further configured
  6. * (e.g. calling `.withDispatcher()` on it)
  7. */
  8. def props(magicNumber: Int): Props = Props(new DemoActor(magicNumber))
  9. }
  10.  
  11. class DemoActor(magicNumber: Int) extends Actor {
  12. def receive = {
  13. case x: Int => sender() ! (x + magicNumber)
  14. }
  15. }
  16.  
  17. class SomeOtherActor extends Actor {
  18. // Props(new DemoActor(42)) would not be safe
  19. context.actorOf(DemoActor.props(42), "demo")
  20. // ...
  21. }

Creating Actors with Props

Actors are created by passing a Props instance into the actorOf factory method which is available on ActorSystem and ActorContext.

  1. import akka.actor.ActorSystem
  2.  
  3. // ActorSystem is a heavy object: create only one per application
  4. val system = ActorSystem("mySystem")
  5. val myActor = system.actorOf(Props[MyActor], "myactor2")

Using the ActorSystem will create top-level actors, supervised by the actor system’s provided guardian actor, while using an actor’s context will create a child actor.

  1. class FirstActor extends Actor {
  2. val child = context.actorOf(Props[MyActor], name = "myChild")
  3. // plus some behavior ...
  4. }

It is recommended to create a hierarchy of children, grand-children and so on such that it fits the logical failure-handling structure of the application, see Actor Systems.

The call to actorOf returns an instance of ActorRef. This is a handle to the actor instance and the only way to interact with it. The ActorRef is immutable and has a one to one relationship with the Actor it represents. The ActorRef is also serializable and network-aware. This means that you can serialize it, send it over the wire and use it on a remote host and it will still be representing the same Actor on the original node, across the network.

The name parameter is optional, but you should preferably name your actors, since that is used in log messages and for identifying actors. The name must not be empty or start with $, but it may contain URL encoded characters (eg. %20 for a blank space). If the given name is already in use by another child to the same parent an InvalidActorNameException is thrown.

Actors are automatically started asynchronously when created.

Dependency Injection

If your Actor has a constructor that takes parameters then those need to be part of the Props as well, as described above. But there are cases when a factory method must be used, for example when the actual constructor arguments are determined by a dependency injection framework.

  1. import akka.actor.IndirectActorProducer
  2.  
  3. class DependencyInjector(applicationContext: AnyRef, beanName: String)
  4. extends IndirectActorProducer {
  5.  
  6. override def actorClass = classOf[Actor]
  7. override def produce =
  8. // obtain fresh Actor instance from DI framework ...
  9. }
  10.  
  11. val actorRef = system.actorOf(
  12. Props(classOf[DependencyInjector], applicationContext, "hello"),
  13. "helloBean")

Warning

You might be tempted at times to offer an IndirectActorProducer which always returns the same instance, e.g. by using a lazy val. This is not supported, as it goes against the meaning of an actor restart, which is described here: What Restarting Means.

When using a dependency injection framework, actor beans MUST NOT have singleton scope.

Techniques for dependency injection and integration with dependency injection frameworks are described in more depth in the Using Akka with Dependency Injection guideline and the Akka Java Spring tutorial in Typesafe Activator.

The Inbox

When writing code outside of actors which shall communicate with actors, the ask pattern can be a solution (see below), but there are two thing it cannot do: receiving multiple replies (e.g. by subscribing an ActorRef to a notification service) and watching other actors’ lifecycle. For these purposes there is the Inbox class:

  1. implicit val i = inbox()
  2. echo ! "hello"
  3. i.receive() should be("hello")

There is an implicit conversion from inbox to actor reference which means that in this example the sender reference will be that of the actor hidden away within the inbox. This allows the reply to be received on the last line. Watching an actor is quite simple as well:

  1. val target = // some actor
  2. val i = inbox()
  3. i watch target

Actor API

The Actor trait defines only one abstract method, the above mentioned receive, which implements the behavior of the actor.

If the current actor behavior does not match a received message, unhandled is called, which by default publishes an akka.actor.UnhandledMessage(message, sender, recipient) on the actor system’s event stream (set configuration item akka.actor.debug.unhandled to on to have them converted into actual Debug messages).

In addition, it offers:

  • self reference to the ActorRef of the actor

  • sender reference sender Actor of the last received message, typically used as described in Reply to messages

  • supervisorStrategy user overridable definition the strategy to use for supervising child actors

    This strategy is typically declared inside the actor in order to have access to the actor’s internal state within the decider function: since failure is communicated as a message sent to the supervisor and processed like other messages (albeit outside of the normal behavior), all values and variables within the actor are available, as is the sender reference (which will be the immediate child reporting the failure; if the original failure occurred within a distant descendant it is still reported one level up at a time).

  • context exposes contextual information for the actor and the current message, such as:

    • factory methods to create child actors (actorOf)
    • system that the actor belongs to
    • parent supervisor
    • supervised children
    • lifecycle monitoring
    • hotswap behavior stack as described in Become/Unbecome

You can import the members in the context to avoid prefixing access with context.

  1. class FirstActor extends Actor {
  2. import context._
  3. val myActor = actorOf(Props[MyActor], name = "myactor")
  4. def receive = {
  5. case x => myActor ! x
  6. }
  7. }

The remaining visible methods are user-overridable life-cycle hooks which are described in the following:

  1. def preStart(): Unit = ()
  2.  
  3. def postStop(): Unit = ()
  4.  
  5. def preRestart(reason: Throwable, message: Option[Any]): Unit = {
  6. context.children foreach { child
  7. context.unwatch(child)
  8. context.stop(child)
  9. }
  10. postStop()
  11. }
  12.  
  13. def postRestart(reason: Throwable): Unit = {
  14. preStart()
  15. }

The implementations shown above are the defaults provided by the Actor trait.

Actor Lifecycle

Image (Asset 1/1) alt=