Akka.NET #3: Actor selections, paths and references – where should you send your messages

By | April 2, 2017

One of the concepts of actor model that could be hard to grasp at the begining is fact that we will not operate on direct reference to actor instance. This can be a bit confusing before you’ll get used to it but by not using any direct references you’re sure to achieve very good level of encapsulation.

Nothing is public

In actor there is no method, field or property that should be public. Everything that actor receives should be delivered inside of a message and any value actor provides should be sent by message. In my actors only public things is static helper for Props in each of actor classes.

If you remember my other posts about akka.NET, you should already know that messages are also immutable, means they state will not change in any way in between sender and sendee. I’ll show you sample message (in fact it is a Command, which is kind of a message I’ll tell you more about when writing about persistence)

As you can see there’s no parameterless constructor and no way to change message state outside of it’s constructor. And actor’s state are mutable only in response of incoming messages.

Where I’m going with this? To one simple fact. Everything is so tightly encapsulated that even if we’ve had direct refence to our actor, we couldn’t do anything with it (well it’s a lie, you could use a reflection but let’s don’t go there). It’s great field for building distributing systems because we won’t have to worry(in most cases)  about managing threads, using TPL directly but most importantly about locking resources or sharing state between threads.

You don’t have reference to your actors. So how exactly are you supposend to send him anything?

IActorRef

When creating actor using ActorOf method you can save refference to object of type IActorRef. This is as close as we should go to actor and it is most preferable way to send him anything. That’s why I’ve saved IActorRefs for my top level actors and every message that I’m sending to their children go through them (they’re forwarding messages and acting as sort of routers). If you can’t recall creating actor here’s sample:

This is first way of obtaining IActorRef to anything. Other way is using Context of any actor where you can access Parent, Child, Sender(of current message) or even Self IActorRefs. You can also create new actor and use their IActorRef. As i’ve mentioned every top actor I have is working as a router and just passing messages to their destination or initilizes new actors if it’s necessary. I’ve desceibed actor creation in other post so please go there if you’re confused. You can grab child of any actor using Context.Child(name) method. Code for reference:

Since IActorRef is just an object, you must remember you can send it as a payload of a message (or just store it) and use provided that way IActorRef to send message to those.

There is one more useful and common way that we can use to obtain IActorRef but first, we must understand how actor paths work.

Actor Paths and ActorSelection

Every actor in our system is part of hierarchy. On top there is our ActorSystem, in my project there are few managers/top-level actor created just after ActorSystem, and below are things like UserActor, TagActor etc. with constructs like StatisticsActor  and other single instance actors dedicated to doing one simple job in the neighborhood. And as you probably already know, you can send messages from any actor to every actor in you system and remote systems. But how to do it?

One way is go through managers. In my project I have ActorModel helper class with static method StartActorSystem that’ll wrap things up. This class is within boundaries of Me20.Core namespace which depends on Me20.Content and Me20.Identity namespaces which contains some bounded context related actors. So in those Content and Identity projects I could add reference to Core project and have access to ActorModel.ActorSystem or ActorModel.SomeTopLevelActorRef , but that would introduce circular dependency and we really don’t want to do that. And there could be things like remote actors of types that we don’t even have in solution. How we should proceed from here?

Let’s start with paths. Every actor have a path, it’s address it’s based on hierarchy of actors and parameter that we’ve been pasing during creation of this address is segment of address to current actor, everything before is address based on hierarchy. Sample actor path is:

akka://MainSystem/user/TagsManager/tagName

“akka://” is our “protocol”(kind of) segment, in this case it means local akka system it could also point to remote systems, “MainSystem” i name of our ActorSystem, “user” means user created and managed part of ActorSystem (in difference with thingthat are being internally managed and created), “TagsManager” is path to one of our top level actors and “tagName” is finally name of our tag actor.

Every actor have path of it’s own. And we can use it to send message to any actor that path we know about. So it’s possible to send message to address akka://System/user/Something/1234. There is one problem with that, we dont know if anyone is listening on the other side of given address. Still we can send messages knowing only absolute part of actor pathusing Context or ActorSystem as our point of orientation.

We can also use current Context to send one message to some relative path ie. all childrend of current actor parent.

Or to sibling with given name:

Or even just part of a name:

I hope you see the pattern now. However without making sure of actor existence it’s like calling random phone number or rather writing postcard to your frind who lived at current address 10 years ago. It’s really possible that your message gets somewhere but that’s all. To get more save IActorRef you need to do one of tho things. First one is send Identify message, receive it in current actor and access it’s sender. Identify message is part of akka.NET and will be recognized by any actor. But let’s skip this because it can be tricky way and simplest one is using method ResolveOne with TimeSpan as a parameter to asynchronously acquire IActorRef from given ActorSelection. Since this method return type is Task<IActorRef> we can use most of TPL tool and/or async/await which makes it really handy and does very similiar operations to sending Identify message with much easier handling. Simplest code sample below:

Summary

We should always try to acquire IActorRef if possible, but if it’s not or we just don’t care if actor exists or not because message is just not so important to worry about we can skip it and use ActorSelection instead it’s also very usefull ife we use relative actor paths to access siblings. As with many things in programming the answer is “it depends” but it’s really necessary to know both ways of acquiring destination for our messages to use them wisely.

PS. I’ve done followup post regarding actor naming requirements. You can read it here.