Creating RESTful Services using Orleans Posted on 13.02.202413.02.2024 By caiti335 After the announce of the Orleans preview, there was a lot of discussion on Twitter. One comment in particular caught my eye. .NET’s actor model uses static factories, RPC Interfaces and code-gen client proxies for comms, WCF all over again: http://t.co/PyIq291Kvh — Demis Bellot (@demisbellot) April 3, 2014 I think this is a bit of a misunderstanding of how Orleans can and should be used in production services, this blog post is an attempt to clarify and demonstrate how to build RESTful, loosely coupled services using Orleans. Orleans Programming Model Orleans is a runtime and programming model for building distributed systems, based on the actor model. In the programming model there are a few key terms. Grains – The Orleans term for an actor. These are the building blocks of Orleans based services. Every actor has a unique identity and encapsulates behavior and mutable state. Grains are isolated from one another and can only communicate via messages. As a developer this is the level you write your code at. Silos – Every machine Orleans manages is a Silo. A Silo contains grains, and houses the Orleans runtime which performs operations like grain instantiation and look-up. Orleans Clients – clients are non-silo code which makes calls to Orleans Grains. We’ll get back to where this should live in your architecture later. In order to create Grains developers write code in two libraries. GrainInterfaces.dll and Grains.dll. The GrainInterfaces library defines a strongly-typed interface for a grain. The Method names and Properties must all be asynchronous, and these define what types of messages can be passed in the system. All Grain Interfaces must inherit from Orleans.IGrain. /// <summary> /// Orleans grain communication interface IHello /// </summary> public interface IHello : Orleans.IGrain { Task<string> SayHello(); Task<string> SayGoodbye(); } The Implementation of the Grains should be defined in the a separate Grains Library. All Grain implementations should implement its corresponding Grain Interface, and inherit from Orleans.GrainBase. /// <summary> /// Orleans grain implementation class HelloGrain. /// </summary> public class HelloGrain : Orleans.GrainBase, HelloWorldInterfaces.IHello { Task<string> HelloWorldInterfaces.IHello.SayHello() { return Task.FromResult(" I say: Hello! " + DateTime.UtcNow.ToLongDateString()); } Task<string> HelloWorldInterfaces.IHello.SayGoodbye() { return Task.FromResult("I say: Goodbye! " + DateTime.UtcNow.ToLongDateString()); } } At compile time code is generated in the GrainInterfaces dll, to implement the code needed by the Silos to perform message passing, grain look-up etc… This code, by default will be under GrainInterfaces/properites/orleans.codegen.cs There are a lot of interesting things happening in this file, I recommend taking a look if you want to understand the guts of Orleans a bit more. Below I’ve pulled out snippets of the generated code. Every GrainInterface defined in the library will have a corresponding Factory Class and GrainReference Class generated. The Factory Class contains GetGrain methods. These methods take in the unique grain identifier and creates a GrainReference. If you look below you will see that the HelloGrainReference has corresponding SayHello and SayGoodbye methods with the same method signature as the Interface. public class HelloFactory { public static IHello GetGrain(long primaryKey) { return Cast(GrainFactoryBase.MakeGrainReferenceInternal(typeof(IHello), 1163075867, primaryKey)); } public static IHello Cast(IAddressable grainRef) { return HelloReference.Cast(grainRef); } [System.SerializableAttribute()] [Orleans.GrainReferenceAttribute("HelloWorldInterfaces.IHello")] internal class HelloReference : Orleans.GrainReference, IHello, Orleans.IAddressable { public static IHello Cast(IAddressable grainRef) { return (IHello) GrainReference.CastInternal(typeof(IHello), (GrainReference gr) => { return new HelloReference(gr);}, grainRef, 1163075867); } protected internal HelloReference(GrainReference reference) : base(reference) { } public System.Threading.Tasks.Task<string> SayHello() { return base.InvokeMethodAsync<System.String>(-1732333552, new object[] {}, TimeSpan.Zero ); } public System.Threading.Tasks.Task<string> SayGoodbye() { return base.InvokeMethodAsync<System.String>(-2042227800, new object[] {}, TimeSpan.Zero ); } } } In an Orleans Client you would send a message to the HelloGrain using the following code. IHello grainRef = HelloFactory.GetGrain(0); string msg = await grainRef.SayHello("Hello Orleans!"); So at this point if you are thinking, this looks like RPC, you are right. Orleans Clients and Orleans Grains communicate with one another via Remote Procedure Calls, that are defined in the GrainInterfaces. Messages are passed via TCP connections between Orleans Clients and Grains. Grain to Grain calls are also sent over a TCP connection if they are on different machines. This is really performant, and provides a nice programming model. As a developer you just invoke a method, you don’t care where the code actually executes, one of the benefits of Location Transparency. Ok stay with me, Deep Breaths. Project Orleans is not trying to re-create WCF with hard coded data contracts and tight coupling between services & clients. Personally I hate tight coupling, ask me about BLFs, the wire-struct in the original Halo games, if you want to hear an entertaining story, but I digress… RESTful Service Architectures Orleans is a really powerful tool to help implement the middle tier of a traditional 3-tiered architecture. The Front-End, which is an Orleans Client, The Silos running your Grains and performing application level logic, and your Persistent Storage. On The Front-End you can define a set of RESTful APIs (or whatever other protocol you want for that matter), which then routes incoming calls to Orleans Grains to handle application specific logic, by using the Factory methods generated in GrainInterfaces dll. In addition the Front-End can Serialize/Deserialize messages into the loosely coupled wire-level format of your choosing (JSON, Protocol Buffers, Avro, etc…). By structuring your services this way, you are completely encapsulating the dependency on Orleans within the service itself, while presenting a RESTful API with a loosely coupled wire struct format. This way the clients can happily communicate with your service without fear of tight coupling or RPC. The below code uses ASP.NET WebApi to create a Front End Http Controller that interacts with the Hello Grain. public class HelloController : ApiController { // GET api/Hello/{userId} public async Task<string> Get(long userId) { IHello grain = HelloFactory.GetGrain(userId); var response = await grain.SayHello(); return response; } // DELETE api/Hello/{userId} public async Task Delete(long userId) { IHello grain = HelloFactory.GetGrain(userId); var response = await grain.SayGoodbye(); return; } } While this is a contrived example, you can see how you can map your REST resources to individual grains. This is the architectural approach Halo 4 Services took when deploying Orleans. We built a custom, light weight, super fast front-end that supported a set of Http APIs. Http Requests were minimally processed by the front-end and then routed to the Orleans Grains for processing. This allowed the game code and the services to evolve independently from one another. The above example uses ASP.NET Web API, if you want something lighter weight checkout OWIN/Project Katana. *HelloGrain Code Samples were taken from Project “Orleans” Samples available on Codeplex, and slightly modified. Tech Insights
2015: A Year in Review Posted on 13.02.202413.02.2024 2015 has been a whirlwind of a year, which started off in a new city, with a new job as the Tech Lead of Observability at Twitter. The year was full of travel spanning 10 states, 3 different countries, and 2 continents. This year I also had numerous opportunities to… Read More
Recommended Engineering Management Books Posted on 13.02.202413.02.2024 Over the past 3.5 years my career has grown and transformed from Individual Contributor (IC) to an Engineering Manager of multiple teams, and all the roles in between as I built the Azure Sphere Security Services (AS3) Team from 2 people to 20 people. I undertook this journey in the… Read More
A Quick Guide to Testing in Golang Posted on 13.02.202413.02.2024 When I started writing Go in May, I found a lot of useful documentation on Getting Started with Go. However, I found recommendations on testing best practices lacking. So I decided to write down what I pieced together, and create a Github Repo of a base project with examples. Essentially… Read More