I have been working with Akka.NET for some time, and I have to admit that it is an amazing piece of technology. It truly simplifies building scalable, concurrent, distributed, and fault-tolerant applications. Thanks to its implementation of the actor model, Akka.NET can help you with designing critical sections of your applications in a non-locking manner. Unfortunately, while it gives you the freedom from having to deal with complex matters like explicit locking or thread management, at the same time, it makes simple things not so obvious. For instance: how to expose your actor based system to the world? You can try to do so by leveraging Akka.Remote package with its location transparency feature, but it would imply that all your clients use Akka.NET as well. It’s one hell of an assumption in this day and age when microservices rule the world. So we go for REST, then. And this is precisely where ASP.NET Core comes to play.
Understanding the example application
You can find the source code of the example application on GitHub. It is a simple bookstore inventory management service that exposes functionalities like:
- adding new books
- finding books by id
- listing all the books available in the system
To meet these extremely sophisticated business requirements, you will use the following actor:
This little fellow listens to 3 types of messages:
GetBooks. Our job is to find a way to route these messages to it. But before we can do so, we will need an actor system to be set up and configured to run within our application.
ActorSystem to rule them all
As you probably know already, there should be only one
ActorSystem deployed per application. It’s vital because
ActorSystems are really heavy objects. It’s an expensive operation to create one. Moreover, as a reference to the underlying Akka.NET framework, the actor system defines the context within all your actors live. In other words, it’s a container for all actor instances. If you decide to kill the actor system, all actors will die with it, and the messages pending in their mailboxes will be lost as well.
The best way to restrict the initialization of a certain class to precisely one object in ASP.NET Core based applications is to register it as a singleton in the IoC container. It’s the right way to go for our actor system, and the following code should do the trick:
The crucial thing to note here is to use
AddSingleton overload which takes a function as an argument. It’s important because the last thing we want is to create the actor system during container configuration. There is a much better place to do so.
IApplicationLifetime provides a neat way to hook into ASP.NET Core application lifecycle events. It was designed to allow developers to perform clean-up during a graceful shutdown of the application. A quick look at its definition reveals that nothing stands against using it to do some initial work after the application has fully started as well.
It looks exactly like what we are after. Now what we have to is to add
IApplicationLifetime as a parameter to Configure method in Startup file.
As a result, a new dependency will be injected at runtime, and we can subscribe to
ApplicationStopping events to start and terminate the actor system respectively.
The code above is rather self-explanatory. The only tricky thing here may be the need to call
Wait method just after we requested our actor system’s termination. It results from the nature of
Terminate method which is asynchronous. You want to wait until Akka performs all the necessary clean-up work before the shutdown procedure can be safely resumed.
Where are actors born?
Now that you know how to fit the
ActorSystem into your ASP.NET Core application, it’s time to think about actually using it to create some actors and send them to work.
In our demo application, there is only one actor -
BooksManagerActor. Bringing it to life is as simple as:
The problem, however, is how to make it available to the rest of the application, and controllers in particular. As we are in ASP.NET Core world probably the first thing that comes to your mind is to register it in IoC container as we already did with
ActorSystem. Unfortunately, it cannot be done that easily. All because of the concept of Actor References.
When you create a new actor, Akka.NET doesn’t give you the reference to the actual instance of the actor you created. All you get back is so-called actor reference. An actor reference (or an actor handle) is a form of proxy, whose purpose is to support sending messages to the actor it represents. You can hate it, or you can love it, but it’s one the most fundamental aspects of how Akka.NET is implemented. This is what makes Akka location transparency feature possible. In our case, however, it simply boils down to the fact that every actor you create will be represented by
It would be fine if you had only one top-level actor is your system (like we do in our demo app), but it’s hardly a case in real-world applications. What’s the problem, then? The problem is you cannot register your actors in IoC container as they are of the same type.
Since I first started learning Akka, I’ve come across several articles which try to address the problem of managing actor references. Most of them suggested to use static classes to cache and expose permanent references to
ActorSystemand top-level actors. While I highly respect most of the authors, especially brilliant guys from Petabridge team, this solution is a big no-no for me.
The solution to this problem I am going to show here is really simple. The idea is to introduce a delegate which will represent a function that takes no arguments and returns
This delegate can then be registered in IoC container with function that returns corresponding actor reference. The container will guarantee that the given actor will be created only once, as it already does for
This actor provider delegate can be injected wherever you like to use a specific actor, for example inside a controller. By invoking it, you will obtain
IActorRef pointing to the appropriate actor instance. To register another actor just define next delegate and proceed as above.
For most cases, I like to unwrap my actor inside the constructor and then assign it as an instance field. It feels more natural to me than invoking provider function every time I want to send a message to the actor.
If you want to query an actor you can do it by using the Ask pattern.
Ask return a
Task, which means it can be awaited, making your method fully asynchronous.
Askfor communication between actors is considered to be an anti-pattern, it is perfectly fine to do so from the controller action. You can try to avoid it as you may, but what you will finally end up is reimplementing
Askfunctionality all by yourself. Ask me how I know. ;)
If you, on the other hand, want to request some operation to be performed by your actor, and:
- you do not care about the response
- the response may be delivered in some unknown future
- there won’t be any response
you should just use
It is considered to be a good practice for this kind of fire and forget asynchronous operations to return 202 (Accepted) HTTP status code from your action methods. This way you are giving your clients a better sense of what’s going on in the system.
In this blog post, you learned how Akka.NET can be combined with ASP.NET Core. If you have any questions or some points are not clear to you, please leave me a comment below!
Subscribe to Havret on Software
Get the latest posts delivered right to your inbox