A few weeks ago I discovered a framework implementing the actor-model: Akka.NET. Since I was quite unaware regarding this programming model, I decided to give it a try. I really like what I saw and the concepts behind the actor-model and this is why I’m creating this blog post.
The actor-model adopts the idea that everything is an actor, an actor is an entity that receive messages, creates other actors and adapts its behavior depending on the message it receives. And the entire system relies on asynchronous communication making it inherently concurrent.
Akka.NET is an open-source project (available on GitHub) bringing the actor-model to the .NET world (C# & F#), it is based on the Akka framework (Scala & Java).
To install Akka.NET for one of your project, you just have to install a nuget package:
Install-Package Akka
Creating your first actor
I will now show you how to use the project to create your first actor-model. You’ll find below the definition of an actor in C#.
public class YellowActor : UntypedActor { private const string ActorName = "YellowActor"; private const ConsoleColor MessageColor = ConsoleColor.Yellow; protected override void OnReceive(object message) { if (message is string) { var msg = message as string; PrintMessage(msg); } } private void PrintMessage(string message) { Console.ForegroundColor = MessageColor; Console.WriteLine( "{0} on thread #{1}: {2}", ActorName, Thread.CurrentThread.ManagedThreadId, message); } }
In this case I created a class that inherits from UntypedActor, this allow me to override the OnReceive method. This way the system knows that this class represents an actor able to treat messages. Creating a basic actor is simple, I don’t need to write too much code.
As you can see the OnReceive method is protected, if I want to use this actor I will have to create an actor system, I cannot just instantiate this class to use it. Akka.NET provides a set of functionalities to deal with the actor model, the ActorSystem is one of them. This is how you create a system and an actor:
var actorSystem = ActorSystem.Create("myActorSystem"); var yellowActor = actorSystem.ActorOf<YellowActor>(); actorSystem.AwaitTermination();
Since my actor is quite simple, I don’t need more code to have an up-and-running actor system with an actor. The WaitTermination method blocks the current thread to avoid it to exit the program, it waits for the actor system to shut down.
Now I can send a message to the actor to see what will happen. To do so I will add the following lines:
Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Starting actor system on thread: {0}", Thread.CurrentThread.ManagedThreadId); yellowActor.Tell("Message to yellow");
Let’s run the application to see what is happening.
The YellowActor has received the message and has printed it, on a different thread, which makes sense since the one that has created the actor system is blocked by this same system.
There is one important concept to know when manipulating actors with Akka.NET, in my example the yellowActor variable is not an instance of YellowActor, it is an IActorRef. Sending messages to actors is always done with reference, the framework will know how to deliver the message to the associated actor.
Adding a second actor
Now that we have a working actor I will add a second one which will be used by the YellowActor. This way I will show you how to “create” an actor from another one. Let’s define a GreenActor:
public class GreenActor : ReceiveActor { private const string ActorName = "GreenActor"; private const ConsoleColor MessageColor = ConsoleColor.Green; protected override void PreStart() { base.PreStart(); Become(HandleString); } private void HandleString() { Receive<string>(s => { PrintMessage(s); }); } private void PrintMessage(string message) { Console.ForegroundColor = MessageColor; Console.WriteLine( "{0} on thread #{1}: {2}", ActorName, Thread.CurrentThread.ManagedThreadId, message); } }
This time I made the class inherits from ReceiveActor allowing me to use the Receive<T> property telling the actor how to handle different types of message, in my case it’s still a string.
Now I will update the YellowActor to use this new actor.
private IActorRef _greenActor; protected override void PreStart() { base.PreStart(); _greenActor = Context.ActorOf<GreenActor>(); } protected override void OnReceive(object message) { if (message is string) { var msg = message as string; PrintMessage(msg); _greenActor.Tell(msg); } }
The actor has an IActorRef field “pointing” toward the green actor. This actor will not only print the message it receives but it will also send it to the green actor. Let’s see what we have now.
The new actor has received the message and treat it on a different thread. I just show you how to easily create actors and how to pass messages between them. You might think that the second actor looks more complicated for the work it does, and you are right! Yet it will come handy in just a minute when we will create a third actor.
One more actor
It is time to create the BlueActor, again this actor will display the message it receives. And will also respond with a message telling how many messages it has displayed. Here is the blue actor:
public class MessageReceived { public int Counter { get; private set; } public MessageReceived(int counter) { Counter = counter; } } public class BlueActor : ReceiveActor { private const string ActorName = "BlueActor"; private const ConsoleColor MessageColor = ConsoleColor.Blue; private int _counter = 0; protected override void PreStart() { base.PreStart(); Become(HandleString); } private void HandleString() { Receive<string>(s => { PrintMessage(s); _counter++; Sender.Tell(new MessageReceived(_counter)); }); } private void PrintMessage(string message) { Console.ForegroundColor = MessageColor; Console.WriteLine( "{0} on thread #{1}: {2}", ActorName, Thread.CurrentThread.ManagedThreadId, message); } }
To send back a message inside an actor, you can use the Sender property which is an IActorRef “pointing” to the actor which sent the message. This is helpful if you have an actor hierarchy where a high-level actor delegates some work to lower-level actors.
It is time to update the GreenActor to make it use the BlueActor and to make it handle the MessageReceived type of message, the following code is added to the class:
private const ConsoleColor ResponseColor = ConsoleColor.DarkGreen; private IActorRef _blueActor; protected override void PreStart() { base.PreStart(); var lastActorProps = Props.Create<BlueActor>(); _blueActor = Context.ActorOf(lastActorProps); Become(HandleString); } private void HandleString() { Receive<string>(s => { PrintMessage(s); _blueActor.Tell(s); }); Receive<MessageReceived>(m => PrintResponse(m)); } private void PrintResponse(MessageReceived message) { Console.ForegroundColor = ResponseColor; Console.Write("{0} on thread #{1}: ", ActorName, Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Receive message with counter: {0}", message.Counter); }
I was able to use Receive again to specify how to handle MessageReceived instances, an actor can handle several types of message. An actor can even send messages to itself using the Self property. Let’s try out our actor system.
All three actors have displayed the message and the green actor has received an answer from the blue actor. Let’s see if the counter is working as expected, we will tell the program to send 5 messages to the yellow actor.
The green actor received a message from the blue actor with a counter of 5, looks good! The actors communicate with each other using different threads.
Time to conclude
This blog post is just an introduction of the actor model using Akka.NET, the project offers a lot of powerful functionalities. I always try to find new ways for developing software and the actor-model offers me a new paradigm I was not familiar with.
For now I think that this programming model can be very helpful in some situations and I will try to post more on the topic, I really like the project so far!
If you want to learn more about Akka.NET I highly recommend you to try the Petabridge’s bootcamp which will help you to understand the concepts behind the actor model and how they are implemented in Akka. You can find the bootcamp for free here. I also made a presentation a few days ago and you can find the slides here.
See you next time!
Great article, Julien,
Here is what we did using Actor Model with Akka.NET: https://www.logicify.com/en/blog/big-data-migration-system-using-actor-model-with-akka-net/
LikeLike
Great intro, thanks for sharing this Julien!
LikeLiked by 1 person
Reblogged this on Dinesh Ram Kali..
LikeLike