The actor model and introduction to Orleans project

21/11/2016

אין תגובות

In the last post the focus was about the Akka.Net framework which is based on the Actor model. In this post I will talk about what is the actor model in the first place and what is it good for. I will also show a code sample which is a bit more complex than last post.

 

What is the actor model?

From Wikipedia, we have the actor model in computer science since 1973 (I haven`t been around yet back then) and its main use is to help us implement concurrent systems. The actor model is built as a single process unit, where it can receive messages and work according to these messages. It will generate more actors when needed and control them, make decisions, and send messages back and forth on the actor tree created.

Real life example
The simplest way to explain the actor model is to give a real life example. Most of us (probably all who reads this) has email address. We get emails and send emails, some of the email makes us do something (e.g. joke email makes us laugh). The email address is the actor address and we are the actors who make decisions by each email. Sometimes we reply a message of our own, sometimes we forward the message to other actors, sometimes we work internally (e.g. make food by the recipe message received), and in other times we just don`t do a thing.

 

Messages
The basis of the actor model is all about messages. Messages need to be able to be sent and received so that each actor will be able to do some decisions and work. Without the messages the actor is nothing. In order for the actor model to be the basis of implementation of concurrent systems, those messages have to be sent in an asynchronous way in a method of fire and forget. Actor do not have to implement hand-shake.

Each actor that wishes to send a message has to know the address of the target actor. The actor can send messages to actors which he has their address, actors which their address was received in messages, and actors that he created.

 

State
Each actor has an internal state it maintains, and that state changes or not when messages arrive from or sent to other actors on the tree. E.g. a Boolean variable which receives an event to change its state to true or to false.

 

So what is it good for

The actor model is good for building concurrent systems and is extensively implemented for use in distributed high-scale applications. As each actor is isolated from other actors on the tree and they do not have the need to share resources, they are the perfect candidate for distributed systems.

As I said in the previous post, actor systems are fault-tolerant. Since an actor can create leaf actors, he can also restart them, re-initialize them, and heal the system in general whenever a fault occurs. This is called the “let-it-crash” philosophy which was introduced by Erlang. This means we do not have to program like we will handle any situation occurs. The parent actor will repair its child actors. So a system which will implement the actor model will benefit to be a fault-tolerant system.

 

Code sample

In this post I will demonstrate the use of the actor model via the Orleans framework. Project “Orleans” is a distributed actor runtime and SDK build by Microsoft.
The code for “Orleans” project can be found in Github at the following link
https://github.com/dotnet/orleans

Each actor in the Orleans framework is called a Grain, and the actor system which creates grains is called a Silo. In the next sample I will create a simple “Hello world” sample as always as these are the best samples to start from.

Start a new Console Application project in Visual Studio. Right click on “References” and select “Manage NuGet Packages”. Now we need to add a few “Orleans” packages so search for “Orleans”. Add the following packages:

  • Microsoft.Orleans.Core
  • Microsoft.Orleans.OrleansRuntime
  • Microsoft.Orleans.OrleansProviders
  • Microsoft.Orleans.OrleansHost
  • Microsoft.Orleans.OrleansCodeGenerator

Let us start by adding an interface name IHello which will inherit from IGrainWithStringKey. This interface will have a single method called SasyHello and will accept a string parameter named “name”. The method will return a Task<string>.

Next we need to implement the interface. Create a class called HelloGrain. Inherit from Orleans.Grain and implement our interface IHello. The implementation should be a simple line which returns a Task with the output of “Hello {name}. How are you?”.

We need to run the Orleans host in its own process. This is why we need to setup AppDomain for the SiloHost object. A simple wrapper for the SiloHost can be found at the Orleans project page at Github. It generally just wraps the initialize, start, and stop command of the SiloHost and is also good for a standalone application to run as host because it has a function to parse command line arguments.

So we will use an AppDomain in our application to tell the wrapper to start and when needed to stop the OrleansHostWrapper object. You can see the entire implementation of the wrapper in the github page of the Orleans project.

Here is the core for the AppDomain with the needed InitSilo and ShutdownSilo methods.

In the InitSilo method we create an instance of the OrleansHostWrapper we got from Github and pass it some arguments. If someone will run this application from command line he will be able to pass command line arguments to the host allowing it to be configured from the outside. Next we run the host.

In the ShutdownSilo method we simply call the Dispose function to end the process. Since the internal OrleansHostWrapper Dispose method is in charge of handling of freeing the resources, we call the GC.SuppressFinalize method.

In the Main method of our application we create an AppDomain that will hold the OrleansHost, and when the application will be near end we will call the ShutdownSilo method from the hostDomain.

Now we need to setup the client that will do all the work. First we need to give it some configuration and we will use the basic configuration given to us as default when we call LocalhostSilo method inside ClientConfiguration object.

Last, we need to have our application actually do something. Let`s add a call to our SayHello method in our grain. Using the GrainClient.GrainFactory we will create an instance of the HelloGrain object and use it to call the method.

Since the SayHello method returns a Task we need to address the Result property to get the actual result of the method call. As we do not have here await to get the result we need to call Console.ReadLine method to have the application wait for the result.

Here is the complete Main method code

 

This was a brief introduction for the actor model, what it is and when is it good for us. Also I gave you a brief introduction for the Orleans project which is based on the actor model. When we are about to build a distributed application we can use the Orleans project to start we a great base.

הוסף תגובה
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *