Project Orleans

Facebook
Twitter
LinkedIn

Never miss a post!

Sign up for our newsletter and get FREE Development Trends delivered directly to your inbox.

You can unsubscribe any time. Terms & Conditions.
Categories

Project Orleans is Microsoft’s take on the actor model. The objective of the project is provide a straightforward approach to building distributed high-scale computing applications. As opposed to other actor based frameworks, the architecture focuses more on scalability and simplicity of implementation, rather than performance. This allows the developer to write very simple, single threaded code with minimal complexity. All heavy lifting is done the framework and underlying architecture.

Current version 2.0 is based on .Net Standard 2.0 and .Net core making it cross-platform. Project Orleans is heavily used by Microsoft in its projects notably by 343 Industries as a platform for all of Halo 4, Halo 5 and Gears of War cloud services.

Although Project Orleans is based on the actor model it uses its own naming convention

In Orleans the basic entity is a grain. A grain is the actor within the framework. A grain is also uniquely identified by a key (grain identity). This allows the framework to guarantee unique execution of each grain. The grain identity can be a long integer, a GUID, a string, or a combination of a long+string or GUID+string.

A Grain can be in two different states either in-memory or persisted. When a grain is activated it is loaded in memory. When it finishes to process requests it goes back to a persisted state. From a programmer perspective all grains are accessible at all time, as if they are always in memory. The framework does all the “black magic” to switch the grains between the states.

A Grain lives in an execution container called Silo. Silos form a cluster that combines resources of multiple physical or virtual machines. If a Grain is not present, it is activated within a silo in a cluster. When it finishes to process requests from other grains or other services. The framework puts the grain in a persisted state. If the grain is activated again it is loaded again in a silo within the cluster. Obviously there is no guarantee that it is loaded in the same silo. However from the perspective of the programmer you don’t care as they the framework will give you impression that the Grain is always in memory.

Another important aspect to note is that the architecture is highly fault tolerant. If a silo dies because a machine managing the silo has a fault, the grain is activated in another silo within the cluster. Magic !!!

Its now time to look at a simple implementation of grain.

First task is the define the Grain interface which define that methods that are exposed by the Grain. Also you will notice that the grain inherits from IGrainWithStringkey. This defines how the grain is uniquely identified. In this case the grain is uniquely identified  with a string. Changing this to IGrainWithGuidkey will require the grain to be uniquely identified by guid and so on.

[code]

public interface IMyGrain : IGrainWithStringKey

{
Task<string> PerformTask(string);
}

[/code]

All methods of a Grain interface must return a Task (for void methods) or a Task<T> (for methods returning values of type T). This means that all the tasks performed within a Grain are asynchronous by design.

The following is the implementation of the Grain.

[code]

public class MyGrain : Grain, IMyGrain

{

Task<string> PerformTask(string value)

{

return Task.FromResult(value);

}

public override async Task OnActivityAsync()

{

await base.OnActivityAsync();

}

public override Task OnDeactivateAsync()

{

await base.OnDeactivateAsync();

}

}

[/code]

The framework also provides two entry points. For when the grain is activated and loaded in memory OnActivityAsync and when it is persisted in storage OnDeactivateAsync.

Getting a reference to a grain instance is done through the type of the grain (its interface), in our case IMyGrain and the unique key that identifies the Grain instance.

The following method is used to create an instance of a Grain.

GrainFactory.GetGrain<T>(key)method, where T is the grain interface and key is the unique key of the grain within the type.

[code]

IMyGrain mygrain = GrainFactory.GetGrain<IMyGrain>("uniquekey");

mygrain.PerformTask("hello");

[/code]

and we are done.

If you went through my posts related to akka.net you will notice how much less code and boilerplate is require to create the Grain/Actor. All the heavy lifting is done by the framework.

I hope you enjoyed this post. In the next post I will cover the concept of setting up a grain client and configuration of silos. If you need assistance or consultancy feel free to contact me on [email protected].

Facebook
Twitter
LinkedIn

Our website uses cookies that help it to function, allow us to analyze how you interact with it, and help us to improve its performance. By using our website you agree by our Terms and Conditions and Privacy Policy.