NOTE: A lot has changed in both the .NET and Akka.NET ecosystems since this post was originally written in 2015 and we have since released an updated blog post: “Best Practices for Integrating Akka.NET with ASP.NET Core and SignalR”.

Lots of folks have been asking about Akka.NET and ASP.NET MVC integration on StackOverflow and in our Gitter chat room, so we thought it was time we created the definitive post on how to integrate these two amazing technologies together.

Note: Everything in this article also applies to Web API, Nancy, WCF, and ASP.NET WebForms.

Use Cases

So when would you want to use Akka.NET and a web framework like ASP.NET together at the same time?

Well, you might be like Joel in our “Akka.NET Goes to Wall Street” case study and need to manage concurrent reads / writes to a shared object model.

If you’re building anything resembling a chat room, web-based game, collaboration software, and more - then congratulations: managing concurrent mutations to shared state is something you’re going to have to do. Akka.NET actors are a tremendously better option than sprinkling your code with locks.

In general, what most people use Akka.NET for in the context of ASP.NET is to communicate with others network services the ASP.NET app might depend on, such as remote Windows Services via Akka.Remote and Akka.Cluster. This is especially useful if you need to do any sort of stateful web application programming, but that’s a story for a different day.

How to Start an ActorSystem in ASP.NET

If you’ve gone through Akka.NET Bootcamp, and you should if you haven’t yet, you know how to start an ActorSystem:

// config automatically loads from App.config / Web.config
var actorSystem = ActorSystem.Create("myactorsystem");
var someActor = actorSystem.ActorOf(Props.Create(() => new FooActor()));

ActorSystems are intended to be long-lived objects, for a couple of reasons. First - they’re expensive. Second - an actor system is the context in which actors exist. If you kill off the actor system, all of the actors running within it die and don’t process any of the remaining messages in their mailboxes.

The ActorSystem object creates all of the infrastructure needed to pass messages, serialize messages, open socket communications, etc… So inside your ASP.NET application you want to create your ActorSystem once and cache the result into a static class.

There are two appropriate areas to do this:

  • Global.asax (preferred)
  • Startup.cs (OWIN startup class)

We prefer Global.asax because you can plugin directly to the lifecycle methods of the ASP.NET application process itself, which makes it easier to cleanly shutdown your ActorSystem when IIS recycles the process.

ASP.NET Global.asax Example

Here’s a demonstration from our WebCrawler ASP.NET MVC sample:

public static class SystemActors
{
    public static IActorRef SignalRActor = ActorRefs.Nobody;

    public static IActorRef CommandProcessor = ActorRefs.Nobody;
}

public class ActorSystemRefs
{
    public static ActorSystem ActorSystem;
}

The two classes above are dinky little static classes that give us a permanent reference to our ActorSystem as well as two top-level actors - top-level actors are the ones we’re going to use to communicate between our controllers and our actors, so references to them need to be stored somewhere where they can be easily accessed. Static classes are, by far, the easiest way to do this.

// Global.asax
public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        ActorSystemRefs.ActorSystem = ActorSystem.Create("webcrawler");

        var actorSystem = ActorSystemRefs.ActorSystem;
        var router = actorSystem.ActorOf(Props.Empty
            .WithRouter(FromConfig.Instance), "tasker");
        SystemActors.CommandProcessor = actorSystem.ActorOf(Props.Create(() =>
            new CommandProcessor(router)),"commands");
        SystemActors.SignalRActor = actorSystem.ActorOf(Props.Create(() =>
            new SignalRActor()), "signalr");
    }

    protected void Application_End()
    {
        ActorSystemRefs.ActorSystem.Shutdown();

        //wait up to two seconds for a clean shutdown
        ActorSystemRefs.ActorSystem.AwaitTermination(TimeSpan.FromSeconds(2));
    }
}

So inside Global.asax we create our ActorSystem on Application_Start and shut it down on Application_End, which gets called when IIS recycles the ASP.NET process or the website is explicitly shutdown.

We also start all of our top-level actors that our web app is going to need and cache their IActorRefs into the SystemActors class so we can call those actors from within SignalR Hubs or ASP.NET controllers.

Nancy Startup.cs Example

The same idea we applied to Global.asax in ASP.NET can also be applied to an OWIN Startup class in Nancy, like this:

/// <summary>
/// OWIN Startup
/// </summary>
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HubConfiguration()
        {
            EnableJavaScriptProxies = true
        };

        app.MapSignalR(config); //enable websocket magic
        app.UseNancy();

        ActorSystemRefs.ActorSystem = ActorSystem.Create("webcrawler");

        var actorSystem = ActorSystemRefs.ActorSystem;
        var router = actorSystem.ActorOf(Props.Empty
            .WithRouter(FromConfig.Instance), "tasker");
        SystemActors.CommandProcessor = actorSystem.ActorOf(Props.Create(() =>
            new CommandProcessor(router)),"commands");
        SystemActors.SignalRActor = actorSystem.ActorOf(Props.Create(() =>
            new SignalRActor()), "signalr");
    }
}

Exact same idea. However, listening for a shutdown event in Nancy to cleanly terminate your ActorSystem depends on how you host your application - if you’re hosting Nancy inside IIS / System.Web you can still use the Global.asax approach above.

If you’re self-hosting in Nancy, you’ll have to write your own shutdown code inside the console app running Nancy.

Configuring Akka.NET and ASP.NET

When you call ActorSystem.Create, Akka.NET will automatically load your HOCON configuration settings from App.config or Web.config via the ConfigurationManager.

To enable this, we need to update Web.config to include the following declaration:

<configSections>
    <section name="akka"
             type="Akka.Configuration.Hocon.AkkaConfigurationSection, Akka" />
</configSections>

This allows us to use the AkkaConfigurationSection inside our Web.config or App.config, which we can now add to the file.

<akka>
    <hocon>
        <![CDATA[
    akka {
        loggers = ["Akka.Event.TraceLogger, Akka"]
        loglevel = "ERROR"
        }
        ]]>
    </hocon>
</akka>

For the purposes of this post, I stripped down Web.config to the bare minimum. You can configure additional loggers, Akka.Persistence, Akka.Remote, and Akka.Cluster inside this configuration section too.

How Actors Co-Exist with Controllers and Other ASP.NET Objects

One important distinction you need to keep in mind when using Akka.NET in combination with a web framework like ASP.NET or Nancy is that the object lifecycle of actors is entirely independent from the lifecycle of your controllers; the former can live forever (in theory) while the latter live for as long as a single HTTP request.

ASP.NET Controllers and Akka.NET Actors exist independently

You can create any number of Akka.NET actors, and because actors are lazy, they’ll just sit there in memory and not do anything. But they will live on, independent of your controllers, action filters, and whatever other pieces of plumbing are employed by your web application.

Actors should, ideally, be re-used - sending a message to an actor is hilariously inexpensive. Akka.NET’s last published throughput benchmark had the framework peaking out around 64 million messages per second between a small number of actors. Creating an actor is cheap too (much cheaper than creating a new controller instance, which you do on every request) but it’s much more expensive than sending a message to an actor who already exists.

How Controllers and Actors Communicate

So once you have actors up and running inside your ASP.NET application… How do you use them?

Well, we use our static class from earlier and call those actors directly from ASP.NET!

public class HomeController : Controller
{
    [HttpPost]
    public ActionResult SendRequest(string uri)
    {
        SystemActors.SignalRActor.Tell(uri, ActorRefs.Nobody);
        return new EmptyResult();
    }
}

Due to the request / response nature of HTTP, there are really only two communication patterns we can use between controllers and actors:

  • Request / Response - send a message to our actors; expect a response back.
  • One-way - fire off a message to an actor; move on immediately without expecting a response.

NOTE: SignalR, because it allows true server-push, makes other communication models like Publish-subscribe and Push-and-Pull more viable inside web applications. Hence why you’ll often see SignalR and Akka.NET used together in published samples.

The above code sample is an example of one-way communication.

But what if we actually wanted to get a response back inside our controller, so we could include it in our response?

Well in that case - we just need to use the Ask pattern inside Akka.NET.

public class HomeController : Controller
{
    [HttpPost]
    public async Task<ActionResult> SendRequest(string uri)
    {
        var str = await SystemActors.SignalRActor.Ask<string>(uri,
            TimeSpan.FromSeconds(1));
        return new ContentResult() {Content = str};
    }
}

The Ask<string> method guarantees that we will receive a string result in response to the uri we sent to SignalRActor within TimeSpan.FromSeconds(1), or otherwise the operation will be timed out.

Ask<string> returns a Task<string>, which means we can use async and await to make this action method fully asynchronous. This is how 99.99% of requests which depend on receiving data from an actor are performed. The other 0.01% are done using the Inbox actor (more on that later.)

Questions?

There you have it - this post covers the majority of basic technical questions we get asked about ASP.NET and Akka.NET integration. If you have any questions about this, please leave us a comment below!

If you liked this post, you can share it with your followers or follow us on Twitter!
Written by Aaron Stannard on August 20, 2015

 

 

Observe and Monitor Your Akka.NET Applications with Phobos

Did you know that Phobos can automatically instrument your Akka.NET applications with OpenTelemetry?

Click here to learn more.