Our thoughts, knowledge, insights and opinions

Overview of Akka Typed

My initial idea for this blog post was to prepare some sample project which shows how typed actors can communicate with each other. But no. You can read about this in the docs. What’s more, it will take few major Akka releases till Akka Typed gets final (it’s experimental now), meaning all of this can have different shape by then.

I would like to emphasize some notable differences made to Akka programming model and my thoughts on these improvements.

Some say Akka in current state has one big drawback

You can send anything you want to an actor and nothing bad happens. It was done that way, because an Actor can change it’s behavior after its creation meaning it’s hard to statically type check this. Akka Typed is the newest attempt at solving the issue, after Typed Actors which are deprecated now. With earlier approaches with Channel (Akka 1.2), TypedChannels (Akka 2.1). There are rumors the Akka team believes this one will reach the final version.

So, what’s different from untyped Akka? First and foremost, with Typed Actors we have to forget about Actor trait. What defines an Actor is it’s behavior wrapped over messages an Actor is going to handle. Testing actors is simpler with Akka Typed - there’s no need to resort to TestKit and spin an actor system. The behavior can be tested in isolation.

Behaviors

Best definition comes from Scaladoc: The behavior of an actor defines how it reacts to the messages that it receives. The message may either be of the type that the Actor declares and which is part of the ActorRef signature, or it may be a system Signal that expresses a lifecycle event of either this actor or one of its child actors.

There is no context.become() as each behavior must return another in reaction to an event - the same or a new one. There are a few types of behaviors and you will find some of them described below:

  • Total - core of all behaviors, allows to change behavior (one of predefined or custom one) and requires to handle all messages of defined type, but ignore system signals
  • Tap - allows to perform some action like logging before desired behavior will be used
  • Partial - allows to declare new behavior using PartialFunction flagging all unmatched messages as Unhandled
  • Static - succinct way to define actor without the need to change to different behavior, Same behavior returned by default
  • Full - as Total, but with Signals support too, all handled as PartialFunction

Behavior decorators

Behavior decorators extract certain variables from surrounding Actor context upon receiving first message or signal, they wrap nested Behaviors.

  • SelfAware - extracts the self ActorRef while receiving the first signal or message
  • ContextAware - extracts the ActorContext while receiving the first signal or message

Events/Signals

Currently Actor’s lifecycle is based on callbacks. It doesn’t sound like a good fit for messaging approach, isn’t it? Now it changes. Each behavior receives ActorContext upon creation where lifecycle is based on Signals, which are handled the same way as regular messages. I think these names are self explanatory.

  • PreStart
  • PreRestart
  • PostRestart
  • PostStop
  • Failed with decisions: Escalate, Stop, Restart, Resume
  • ReceiveTimeout
  • Terminated

Communicating with other actors

With Typed Actors we lose sender() method, which turned out to be problematic or error prone as the real sender always depends on the context in which it is being invoked. All actors we wan’t to communicate with must be passed through as part of the message.

Please take a look at following example.

[1] - For untyped actor, you can use context.become() to pass state and maintain it, as alternative to less functional approach where state is handled in a mutable var variable. As each message must return a behavior, you can pass the state to it directly.

[2] - creates child actor to handle defined messages and pass them accordingly to parent

[3] - behaviors can also receive other Actor references created either by main/parent actor or found by Receptionist

[4] - return new behavior

[5] - only expose FoodOrdered to the outside, so internal command StoreFood from [3] is handled only by this Actor itself

A Receptionist

A Receptionist system wide Actor is the replacement for context.actorSelection(). Other Actors can register their identity together with protocols they implement, allowing them to be found later. Here’s an example

Personally I find this code a bit confusing.

You create a Receptionist and you register an Actor of given behavior for a given key. Then, you have to define where to reply with confirmation that given Actor was indeed registered. You do this by passing to replyTo argument an Actor of type ActorRef[Receptionist.Registered[FridgeCommand]] which sounds like type for already registered Actor. That Actor will receive message Registered[T](key: ServiceKey[T], address: ActorRef[T]) as registration confirmation.

If you want to find what is already registered, you have to tell the Receptionist as asking is forbidden. You again have to pass replyTo argument, but now an Actor of type ActorRef[Receptionist.Listing[FridgeCommand]] which sounds like a result of the Find command to me.

Keep in mind that both Actors I passed to replyTo arguments are of different types, meaning two different Actor behaviors have to register and look for an Actor, but they must be of the same command type.

It’s going to make a lot of noise to find an Actor while processing a message as you will have to prepare various Actor behaviors or Actors and send few messages just to get an Actor you want to send message to. Looks like a step toward abandoning ask.

Playing on your own

Typed module is not yet released in any lib version so you have to build it by yourself. You will need few more tools to install before building Akka by yourself. Then, after you clone Akka source code from master branch build libs with

sbt -Dakka.parallelExecution=true -Dakka.scaladoc.diagrams=false publishLocal

Summary

I see more verbose code with Typed Actors, especially the way you work with Receptionist to register and find actors, but having the ability to check Actor definition correctness at compile time is a huge advantage. Because messages are responsible for propagating replyTo information, I believe some patterns will need to emerge to make our code more DRY. Anyway, this is great achievement toward more type safe Akka future.

I’d like to thank Roland Kuhn and Konrad Malawski for their feedback of draft version of this post.

Thanks for reading - @LeszekGruchala

You like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.