Thursday, September 13, 2012

Wiring up TopShelf, Windsor and EasyNetQ

EasyNetQ is my simple to use .NET API for the awesome RabbitMQ messaging broker. Architecting system around a message bus involves writing many small focussed components that sit on the bus waiting for messages they care about to arrive. These are best implemented as Windows services. My favourite way of implementing Windows services is to use TopShelf. This very nice open source library grew out of the excellent MassTransit project. It makes writing windows services super easy; you simply create a console project, “install-package Topshelf”, and use the neat fluent API to describe your service. An IoC container should be at the heart of any but the simplest .NET application. If you’ve not used an IoC container before I talked about why you should several years ago. There are quite a few IoC containers to choose from, my weapon of choice is Castle Windsor.

In this post I want to show how I wire up TopShelf, Windsor and EasyNetQ.

The Zen of IoC says that any application that uses an IoC container should reference it in only two places. First to create an instance of the container that lasts the lifetime of the application. Second to resolve a root service from the container. All the other dependencies are supplied invisibly by the container itself. This magic is what makes IoC containers such awesome and essential frameworks. Following this rule, in the Main() function we create the IoC container and resolve the root service of our application, in this case IVaultService, all within the fluent API provided by TopShelf.

public class Program
{
public static void Main(string[] args)
{
// create the container and run any installers in this assembly
var container = new WindsorContainer().Install(FromAssembly.This());
// start of the TopShelf configuration
HostFactory.Run(x =>
{
x.Service<IVaultService>(s =>
{
// resolve the root IVaultService service
s.ConstructUsing(name => container.Resolve<IVaultService>());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc =>
{
tc.Stop();
// with Windsor you must _always_ release any components that you resolve.
container.Release(tc);
// make sure the container is disposed
container.Dispose();
});
});

x.RunAsLocalSystem();

x.SetDescription("Vault service.");
x.SetDisplayName("My.Vault.Service");
x.SetServiceName("My.Vault.Service");
});
}
}

A cardinal rule of Windsor is that you must release any components that you resolve. Windsor tracks any components that implement IDisposable and ensures that Dispose is called no matter where the component gets resolved in the dependency graph, but you need to call Release for this to happen correctly. The ‘tc’ variable is the instance of IVaultService that gets resolved in the ‘ConstructUsing’ call, so we can use it in the Release call.

What about EasyNetQ? The Zen of EasyNetQ says that you should create a single instance of IBus that lasts the lifetime of the application. Now we could have created our IBus instance in Main() alongside the TopShelf setup and newing up the container, but since we’ve got a container we want it to manage the lifetimes of all the components used in the application. First let’s create a simple factory method that gets the connection string for EasyNetQ and creates a new instance of IBus:

public class BusBuilder
{
public static IBus CreateMessageBus()
{
var connectionString = ConfigurationManager.ConnectionStrings["easynetq"];
if (connectionString == null || connectionString.ConnectionString == string.Empty)
{
throw new VaultServiceException("easynetq connection string is missing or empty");
}

return RabbitHutch.CreateBus(connectionString.ConnectionString);
}
}

Now we can write our IWindsorInstaller to register our services:

public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IVaultService>().ImplementedBy<VaultService>().LifestyleTransient(),
Component.For<IBus>().UsingFactoryMethod(BusBuilder.CreateMessageBus).LifestyleSingleton()
);
}
}

Note that we tell Windsor to create our IBus instance using our factory with ‘UsingFactoryMethod’ rather than ‘Instance’. The Instance method tells Windsor that we will take responsibility for the lifetime of the service, but we want Windsor to call Dispose when the application shuts down, UsingFactoryMethod tells Windsor that it needs to manage the IBus lifestyle. We declare it as ‘LifestyleSingleton’ because we only want a single instance of IBus for the entire lifetime of the application.

Now we can reference IBus in our IVaultService implementation:

public interface IVaultService
{
void Start();
void Stop();
}

public class VaultService : IVaultService
{
private readonly IBus bus;
public VaultService(IBus bus)
{
this.bus = bus;
}

public void Start()
{
bus.SubscribeAsync<MyMessage>("vault_handler", msg =>
{
// handle the message here
});
}

public void Stop()
{
// any shutdown code needed
}
}

Here we are simply subscribing to MyMessage in the Start method of VaultService. I would probably also have an IMyMessageHandler service referenced by IVaultService to do the message handling itself.

So there you have it, a simple recipe for using these three excellent OSS projects together. As a side note it’s worth pointing out that they play together without depending on each other directly. I think this is the way to go with OSS components; they should provide points that allow you to plug in other pieces without mandating them.

4 comments:

tatabánya said...

as always, ALT.NET is better then the official MS solutions. Thank you guys!

Chris Patterson said...

I recommend moving the container initialization to the ConstructUsing() block, so that all AppDomain specific setup code is done within the creation of the service. Otherwise, future plug-ins to Topshelf may not behave as expected.

Just FYI, I should probably update the documentation to include this guidance.

Neil said...

In a case where you might need a database context per message that arrives how would you accomplish that?

I would've used the service parent (vault service) to subscribe to the messages then on the message arriving use the IOC container to resolve a message handler which might do some database operations then dispose once it was done.

Just interested on your take here as that would break the rule of only calling the IoC container twice.

Mike Hadlow said...

Neil, If you're using Windsor, there's still no need to access the container itself, which is very much an anti-pattern. You can use a typed factory instead: http://docs.castleproject.org/Default.aspx?Page=Typed-Factory-Facility-interface-based-factories&NS=Windsor