This is the first post in the series were I’m actually getting into some code. You will be able to follow along the journey of creating a practical web application using Microsoft Orleans. This first post is really just setting the ground level to get familiar with the basics of Grains and Silos. By the end of this post I’ll have a simple demo app that is a functioning ASP.NET Core as our client/frontend with a Orleans Silo hosting our Grains.
Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Blog Post Series:
- Part 1 – Practical Orleans
- Part 2 – Grains and Silos
- Part 3 – Smart Cache Pattern
- Part 4 – Event Sourced Grain
- Part 5 – EventStore for Grain Persistence
Grains and SilosFor this blog post and throughout this series I’m going to be creating a web application with ASP.NET Core. Our ASP.NET Core application will be our frontend and Orleans sits as a stateful middle tier in front of our data storage. Now to break down the two pieces we need to get started with Orleans are Grains and Silos. First let’s take a look at what Grains are.
Grains are the key primitives of the Orleans programming model. Grains are the building blocks of an Orleans application, they are atomic units of isolation, distribution, and persistence. Grains are objects that represent application entities. Just like in the classic Object Oriented Programming, a grain encapsulates state of an entity and encodes its behavior in the code logic. Grains can hold references to each other and interact by invoking each other’s methods exposed via interfaces.One other interesting note about grains is that they are automatically managed by Orleans. You don’t have to worry about instantiating or managing them. Orleans terms them virtual actors as apposed to traditional actors.
SilosWhere do Grains live? Silos of course. Silos are what host and execute Grains. Again, Grains are your objects that expose behavior and encapsulate state. Orleans creates your grains in the Silo and executes them here. Your client code will reference only interfaces that your grains implement. Don’t worry, sample code coming up. Oh and of course, you can also have a cluster of silos.
CodeNow that we know the basics, let’s create a really simple demo just to get our feet wet and get a fully functioning app running. Note: Remember to BUILD once you create any project as there is some codegen that occurs. Do this before you start freaking out about types missing.
Grains ProjectFirst we are going to create a project called
Grainsfor our grains. Here’s a netstandard2.0 project with references to a couple Orleans packages. Note as of this blog post, I’m using 2.0.0-beta2 which supports .NET Standard 2.0. Now we can create our first Grain. I’m going to create a grain that simply increments a number within the grain. Yes that simple.
Silo ProjectNow that we have a grain, we need a Silo. Create a .NET Core 2.0 console application called
Silo. Here’s the cs project with the relevant NuGet packages as well as Reference to our Grains project. As you can see, we also have a OrleansConfiguration.xml that needs to be included in the output. This is configuration file we will use for hosting our Silo. Lastly, we need to start our Silo in our Program.cs
ASP.NET CoreNow let’s create a client that will interact with our Grains. Create a new ASP.NET Core running .NET Core 2.0 application called
WebI’ve got a couple other dependencies referenced. Botwin and Polly. Polly is a fault handling library and Botwin is Middleware for ASP.NET Core that allows Nancy type routing. The reason we want to use Polly is for our initial connection to the Silo. Since we might running multiple projects from Visual Studio/Rider/Code/Whatever, this will help since the Silo might not be ready by the time our ASP.NET fires up. Here is our Startup.cs that creates a singleton of our the Orleans IClusterClient. It is thread-safe and intended to be used for the life of the application. Now we can create a Botwin module to handle two different endpoints. A GET request will return what our current count is, and a POST will increment our count by 1. We simply inject our
IClusterClientwhich is used to call
GetGrain<T>. Once we have our grain, we can call the relevant methods defined on the grain interface.