Update: If you want to read about Topshelf, please go to the updated guide to Topshelf, which covers how to get started with Topshelf 3
This is the first post in a series of posts on the open source service bus implementation, MassTransit. In order to push myself through learning it, I will do these posts as I go and document stuff I learn on the way.
This first post will be about one thing, that you’ll probably end up doing quite a few times while building your awesome messaging-based distributed system: Creating a service.
Check out TopShelf and build it
I’ve described how to use TopShelf before, but a lot has changed since then. TopShelf, at the time of writing, is now in version 2.2.1.0, and since 2.0 it has a feature called “shelving”, which is the ability to host any number of services beneath one Windows Service – just like IIS hosts web apps. This is pretty awesome, but today I’ll just show “the old API”, which IMO is still pretty slick!
First, make sure you have a working Ruby installation with Albacore ( gem install albacore). You’ll need these to build Topshelf.
Next, git clone git://github.com/phatboyg/Topshelf.git and build.bat. That should leave a bunch of binaries in the NET35 and NET40 folders beneath build_output. This is open source, baby!
Put your app on the TopShelf
Create a new “Console Application” and make sure to target the full .NET 4 framework (and not that bastard .NET 4 Client Profile thingie, which is default…)
Now add a reference to Topshelf.dll and log4net.dll. Punch in something like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Program { static void Main() { HostFactory.Run(x => { x.SetDescription("MyBackend-description"); x.SetDisplayName("MyBackend-display-name"); x.SetServiceName("MyBackend-service-name"); }); } } |
This is all the necessary stuff to create a fully functional program, that can be run and debugged by running the resulting .exe – either from the command line, or with F5 inside Visual Studio.
Moreover, it can be installed as a Windows Service by going to the command line (as an administrator!) and executing NameOfYourApp.exe install. On my machine, I got this output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
C:\temp\TransitDemo\Backend\bin\Debug>Backend.exe install Running a transacted installation. Beginning the Install phase of the installation. Installing service MyBackend-service-name.. Service MyBackend-service-name has been successfully installed. Creating EventLog source MyBackend-service-name in log Application... The Install phase completed successfully, and the Commit phase is beginning. The Commit phase completed successfully. The transacted install has completed. |
Now I can net start MyBackend-service-name and net stop MyBackend-service-name on the command line, or I can go to Windows’ service control snap-in and pull the usual levers to control it. Nifty!
Now I have a perfectly functioning Windows Service that does nothing at all! Let’s output some stuff to see where we’ll put our code in a short while. Try extending the x => { ... } lambda to make the program look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Program { static void Main() { HostFactory.Run(x => { x.Service<MyBackend>(c => { c.SetServiceName("MyBackend"); c.ConstructUsing(() => new MyBackend()); c.WhenStarted(d => d.StartMyBackend()); c.WhenStopped(d => d.StopMyBackend()); }); x.SetDescription("MyBackend-description"); x.SetDisplayName("MyBackend-display-name"); x.SetServiceName("MyBackend-service-name"); }); } } |
and a class that looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyBackend { public void StartMyBackend() { Console.WriteLine("Started!"); } public void StopMyBackend() { Console.WriteLine("Stopped!"); } } |
Now, when I press F5, wait a little, and press Ctrl + C, I get this output on the console:
1 2 3 |
Started! Stopped! Press any key to continue . . . |
Awesome, huh?
I really like how TopShelf now uses a Func<TService> to construct its service instance via ConstructUsing, instead of depending on the common service locator as the old TopShelf did. This way, I can pull the instance from an IoC container if I want.
In the next post I will take a look at how to send a message from a web application when someone tries to register as a user.