Code that accompanies this article can be downloaded here.
In the same time when first object-oriented languages were emerging, another concept inspired by general relativity and quantum mechanics was taking shape – actor model. In general terms, the Actor model was defined 1973. and was developed on a platform of multiple independent processors in a network. Similar to the object-oriented approach, this essentially mathematical model, revolved around the concept of actors. An actor is the smallest structural unit of Actor model, and just like objects, they encapsulate data and behavior. Indifference to objects, however, actors communicate with each other exclusively through messages. Messages in actors are processed in a serial manner. According to the full definition of actors, they can do three things:
- send a finite number of messages to other actors
- create a finite number of new actors
- designate the behavior to be used for the next message it receives
Some of the authors are claiming that actors are actually the most stringent form of objects. Let’s not forget, that objects in Smalltalk-80 could hold state, and send and receive messages, and that does sound awful like the definition of an actor. But apart from that, we can definitely see that there is a great benefit in this model. Especially in concurrent, parallel processing environments and distributed systems. This is due to the fact that actors can affect each other only using messages, and by that, all locks are eliminated. Also, we can find a use for this concept in rising world of microservices. We can consider that each microservice is, in fact, an actor in its own process.
What is great about this model is that we can apply best object-oriented practices on it. It seems that it is natural to use actors in combination with Single responsibility principle, and make each actor do one thing (again pushing us to the concept of microservices). Also, we should notice the importance of messages. They are no longer just carriers of data, but also in a more abstract manner, carriers of behavior. What an actor will do is depending on what message it received. This brings us to the fact that in actor systems, messages should be kept immutable, so they don’t change in the middle of processing and by that affect behavior of the system. Also, this way race conditions would be minimal.
Another benefit of these systems is that they are inherently asynchronous. This can be considered limitation too because the synchronous behavior is harder to achieve.
Akka is toolkit which allows us to create actor system in an efficient and simple way in .NET environment.
To start with Akka.Net, you should first install the package in your project, using Package Manager Console:
Also, to avoid warning about deprecated serialization, install Hyperion package too:
Install-Package Akka.Serialization.Hyperion -pre
and add this to your App.config file:
Simple Use Case
When working with actor system, the first thing we need to define is a message type on which actor will react to.
Once that is defined, actor class can be created, by implementing abstract ReceiveActor class:
And consume that actor like this:
How about something more complicated
Ok, that was one easy example to get you started on how Akka works in general. But let’s consider something a little bit more complicated. Let’s make the system that will collect data about how long each reader was reading the article. The system will look like something like this:
It will contain next actors:
- Blog Actor – Drives the whole system and receives messages from the simulated frontend. It will delegate messages to the rest of the system.
- Reporting Actor – Gathers data from users and blog, and displays data to console.
- Users Actor – Parent of individual user actors, used to delegate messages to correct user.
- User Actor – Calculates how much time has user spent on reading the certain article and forwards that information to Reporting Actor.
In order to drive the whole system there will be tree types of messages:
- StartedReadingMessage – This message will indicate that user started reading the article.
- StopedReadingMessage – This message will indicate that user stopped reading the article.
- ReportingMessage – This message will be sent from User Actors
Since they all carry similar information there is base Message class. Here is its implementation:
We can see that base message contains information about the user and about the article. Rest of the messages are used for containing information about action which is performed:
The main program drives this simulation by initializing system as a whole and sending messages to the Blog Actor:
As mentioned before, Blog Actor delegates messages to the rest of the actors. It is also in charge of creating Users Actor and Reporting Actor. You may notice the use of the Context property of the actor, which is used for creating child actors. Also, there is use of Props configuration class, which specify options for the creation of actors.
Users Actor caches information about users, and routes messages to each individual User Actor.
Implementation of User Actor goes as follows:
And last, but not the least here is the implementation of Reporting Agent. It gets data from each individual User Actor, and from Blog Actor, and calculates time spent on each blog post, and the number of views on each blog post.
This is how the result of this simulation looks like:
Actor Model gives us a different way of solving problems. Once you get into the message-driven mindset, you’ll find the Actor Model to be of great value when it comes to designing large-scale, service-oriented systems. On the other side, Akka.NET gives us a framework in which we can create these systems fairly easy. Here we covered just basic uses of Akka.NET, but it has many more features that can help you.
Read more posts from the author at Rubik’s Code.