highway2

Typed actors with routing

This is a blog post of our Code Reading Wednesdays from Codacy (http://www.codacy.com): we make code reviews easier and automatic.

A few weeks ago we wrote about typed actors and how we built our architecture around them. This week we are going to explain how we use them with routing to scale our system.

With Typed Actors, like with normal Actors, you have an external API that will delegate method calls asynchronously to a private instance of the implementation. The advantage is that Typed Actors have a static contract and you don’t need to define your own messages, although there are some limitations on what you can do, i.e. you cannot use routing as in normal actors.

With our architecture using typed actors, and rolling back to untyped actors not being an acceptable solution, we decided to invest some time researching for a proper way to achieve typed actors with routing.

Research

After a lot of documentation reading and trial/error we could not find any working solution that could meet our needs, so we started searching for other similar cases on the web.

The problem was already common within the Akka community and we found a lot of suggestions but not a fully working solution.

A common solution was to create a sequence of actors and let them be routed by an untyped actor. As you may think, that would break our main goal of having typed actors and would throw as back to the traditional Akka message system.

This solution is not useless because it solves part of the problem, so we decided to work with it to meet out needs. The next step was to start looking for a way to connect one of our typed actor with the list of actors to route and we found that, even thought a typed actor can’t have routing, it could receive a untyped router.

Solution

The solution is a pretty simple composition of the two options I explained before.

To start we need a group of typed actors, a sequence of the actors you want to route. These are the actors that’ll do the actual work.

To manage those actors we need to create an untyped actor that routes them, a simple actor with any of the default Akka routers or even with one you define, and then pass the typed actors you created before as workers.

To complete the typed actors router we obviously need a typed actor, so we can have a typed interface to the untyped actor as if we have a single one. This is achieved by creating a typed actor equal to the ones in the sequence, but in this case you pass him the router you created in the second step, and that will have a typed actor set up.

Implementation

Since we use Scala, we’re providing a simplified Scala implementation.

Starting by the typed actors that will be routed:

  val actors = (1 to actorCount).map {
    i =>
      TypedActor.get(system).getActorRefFor(TypedActor.get(system).typedActorOf(TypedProps(Reflect(system).actorClassFor(className.get)).withTimeout(timeout)))
  }

Then the untyped actor that will route the previous actors:

val router = system.actorOf(Props.empty.withRouter(SmallestMailboxRouter.create(actors.toIterable.asJava)))

And to finish the typed actor that will be the interface to the routing system:

TypedActor(system).typedActorOf(TypedProps(Reflect(system).actorClassFor(className.get)).withTimeout(timeout), router)

And now a complete example:

123456789101112131415161718
val defaultAkkaTimeout = 60
val defaultNumberOfActors = 10
def getComponentActor[T <: AnyRef](timeout: akka.util.Timeout = Timeout(Duration(defaultAkkaTimeout, SECONDS)))(implicit m: Manifest[T]): T = {
val className = this.classCache.get(m.runtimeClass.getName)
val actorCount = defaultNumberOfActors
val actors = createRouterActors(actorCount,className,timeout)
val router = system.actorOf(Props.empty.withRouter(SmallestMailboxRouter.create(actors.toIterable.asJava)))
TypedActor(system).typedActorOf(TypedProps(Reflect(system).actorClassFor(className.get)).withTimeout(timeout), router)
}
def createRouterActors[T <: AnyRef](actorCount:Int, className:Option[String], timeout:akka.util.Timeout)(implicit m: Manifest[T]): Seq[ActorRef] = {
(1 to actorCount).map {
i =>
TypedActor.get(system).getActorRefFor(TypedActor.get(system).typedActorOf(TypedProps(Reflect(system).actorClassFor(className.get)).withTimeout(timeout)))
}
}

The code is quite simple and allows us to use a beautiful architecture. With this, we have a system that is easily scalable and we are still using Akka’s typed actors.

That’s it for this week. Hope you find this useful and can use it in your projects.

Rodrigo (@rtfpessoa)


Brought to you by the makers of Codacy (http://www.codacy.com): an automated code review tool focused on giving you code analysis results that matter.
We simplify and save time of your code reviews and pull requests.

Follow us at https://twitter.com/codacy