Before We understand Orleans We need to take a look at normal Scalable Applications. Below is the Diagram that shows it
Scalability refers to an application’s capacity or ability to grow with demand without degrading performance or disrupting the user experience. When you have a scalable application, you can add more resources when needed, which improves your ability to serve customer requests under heavy load, as well as reduce downtime due to server crashes.
Problem in above Diagram
In Microsoft We have services which end up being very large and they often need some kind of way to coordinate with each other without putting too much pressure on lets say a database and without having lot of concurrency issues. Statelessness is required for scaling. Database also adds latency
Statelessness means that every HTTP request happens in complete isolation. When the client makes an HTTP request, it includes all information necessary for the server to fulfill that request. The server never relies on information from previous requests.
As Orleans are based on Actor Model. but unlike actors in more traditional actor systems (Erlang, Akka), Orleans grains are virtual actors. The biggest difference is that physical instantiations of grains are completely abstracted away and are automatically managed by the Orleans runtime.
Small glimpse of Actor Model
A programming model for distributed systems and parallel processing, wherein the “actor” is the primary element of computation similar to an “object” in object-oriented programming (OOP). An actor receives a message and generates output as a self-contained concurrent operation.
What is Orleans
Orleans is a .NET Core, cross-platform framework for building robust, scalable distributed applications. It was created by Microsoft Research and is based on an actor model which provides an innovative approach to simplify the design of highly performant, distributed systems for the cloud. It has a programming model that solves much of the complexity associated with architecting scalable, highly-parallel, distributed systems
This is how a typical Orleans Application looks like. Orleans minimizes latency problems as well my maintaining its own state. So Database calls are not required every time rather once time
Main Features
- Framework of distributed applications
- Build and operate scalable, fault tolerant applications that are composed of distributed objects, called grains
- Model Your application using grains
- Objects have id, behavior and state
- Build on Dot net
Terms associated with Orleans
- Grains — The “virtual actors” and/or “primitives” that are described in the actor model definition above. Grains are the objects that actually contain your logic that is to be distributed. Each individual grain is guaranteed to function in a single-threaded execution model as to greatly simplify the programming, and avoid race conditions. The grains are written in an asynchronous manner, and are intended for very fast running operations .
- Silos — The area where your “grains” are kept. A silo can contain many grain types, as well as many instantiations of those types.
- Clusters — A collection of silos. This allows for the “scale out” portion of Orleans. If more or less resources are needed, you can simply register or kill silos on your cluster. Scaling made easy!
More About Grain
Grains are the key primitives of the Orleans programming model. Grains are the building blocks of an Orleans application, they are microscopic units of isolation, distribution, and persistence. Grains are objects that represent application entities. Just like in the Object Oriented Programming, a grain encapsulates state of an entity and encodes its behaviour in the code logic. Grains can hold references to each other and interact by invoking each other’s methods exposed via interfaces.
Orleans goal is to greatly simplify building a scalable application and eliminate most of the concurrency challenges
- By not sharing data between grains instances except via message passing.
- By providing the single-threaded execution guarantee to each individual grain.
A typical grain encapsulates state and behavior of a single entity (for example a specific user or a device or a session).
Grain Identity
An individual grain is a uniquely addressable instance of a grain type (class). Each grain has a unique identity, also referred to as a grain key, within its type. Grain identity within its type can be long integer, GUID, string, or combination of a long+string or GUID+string.
Accessing a Grain
A grain class implements one or more grain interfaces, formal code contracts for interacting with grains of that type. To invoke a grain, a caller needs to know the grain interface that the grain class implements that includes the method that the caller wants to call and the unique identity (key) of the target grain. For example, here’s how a user profile grain can be called to update user’s address if email is used as a user identity.
var user = grainFactory.GetGrain<IUserProfile>(userEmail);
await user.UpdateAddress(newAddress);
A call to GetGrain is an inexpensive local operation of constructing a grain reference with an embedded identity and type of the target grain.
Creating Orlean Application
An Orleans application consists of a few separate pieces, generally all as separate projects:
- Grain interfaces
- Grain implementations
- Orleans Silo host
- Orleans Client
GrainInterfaces / Grains csproj
Microsoft.Orleans.Core.Abstractions
Microsoft.Orleans.OrleansCodeGenerator.Build
Client csproj
Microsoft.Extensions.Logging.Console
Microsoft.Orleans.Client
Server csproj
Microsoft.Extensions.Logging.Console
Microsoft.Orleans.Server
Grain State and Persistence
The “Orleans” runtime provides a build in support for persistence of the state of a grain (actor). That means, developer can define a part of the grain to be persistable.
This is very important, because internally “Orleans” does not offer common object oriented command “new”, which can be used to create the grain.
Goals
- Allow different grain types to use different types of storage providers (e.g., one uses Azure table, and one uses an ADO.NET one) or the same type of storage provider but with different configurations (e.g., both use Azure table, but one uses storage account #1 and one uses storage account #2)
- Allow configuration of a storage provider instance to be swapped (e.g., Dev-Test-Prod) with just config file changes, and no code changes required.
- Provide a framework to allow additional storage providers to be written later, either by the Orleans team or others.
- Provide a minimal set of production-grade storage providers
- Storage providers have complete control over how they store grain state data in persistent backing store.
[StorageProvider(ProviderName="store1")]
public class MyGrain<MyGrainState> ...
{
...
}
<OrleansConfiguration xmlns="urn:orleans">
<Globals>
<StorageProviders>
<Provider Type="Orleans.Storage.MemoryStorage" Name="DevStore" />
<Provider Type="Orleans.Storage.AzureTableStorage" Name="store1"
DataConnectionString="DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1" />
<Provider Type="Orleans.Storage.AzureBlobStorage" Name="store2"
DataConnectionString="DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2" />
</StorageProviders>
For more information : https://github.com/L-n-G-Consultancy/orleans-sample-code