REQUEST A DEMO

Controlling Application Lifetime In Topshelf

One of Topshelf’s best features is the ability to easily host one or more services running side by side. In this post we will create a Topshelf service which will shut down the application. Lets take a look at how it is done.

 

 

Coordinating the Service

 

Let’s create a fake Health Monitoring service which will, sometime in the future, tell Topshelf to shut the application down. The Topshelf hook for controlling the state of all the services it is hosting is a type called IServiceCoordinator. Here is the code for our health monitor.

public class HealthMonitoringService : IHealthMonitoringService
{
  private readonly IServiceCoordinator _serviceCoordinator;
  private readonly ILogger _logger;
  private Timer _timer;
  private int _healthCheckCount = 0;
 
  public HealthMonitoringService(IServiceCoordinator serviceCoordinator, ILogger logger)
  {
    _serviceCoordinator = serviceCoordinator;
    _logger = logger;
  }
 
  public void Start()
  {
    _logger.LogWarn("The Health Monitor is starting up");
 
    _timer = new Timer(HealthCheck, this, TimeSpan.Zero, TimeSpan.FromSeconds(5));
  }
 
  private void HealthCheck(object state)
  {
    //determine if the service should stop. 
    var shouldStopService = (_healthCheckCount++ > 2); 
 
    _logger.LogDebug("We are {0}going to stop the service.", (shouldStopService) ?
"" : "not "); if(shouldStopService) { _logger.LogWarn("The Health Monitor is stopping the windows service."); _serviceCoordinator.Stop(); } } public void Stop() { _healthCheckCount = 0; _timer.Dispose(); } }

 

This service uses a timer to do a “healthcheck” every 5 seconds. The health check will tell the service coordinator to stop after 3 health checks. We also do a bunch of logging which will give us something to look at while the application is running.

Configuring Topshelf

 

Next up we need to register our service with Topshelf telling it how create and start up our health monitor.

internal class ProgramExample
{
  private static void Main(string[] args)
  {
    Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
 
    var container = new Container(cfg =>
    {
      cfg.AddRegistry();
      cfg.For().Use();
    });
 
    var runConfiguration = RunnerConfigurator.New(config =>
    {
      config.ConfigureService(service =>
      {
        service.Named("Health Monitor");
        service.HowToBuildService(builder => container.GetInstance());
        service.WhenStarted(s => s.Start());
        service.WhenStopped(s => s.Stop());
      });
    });
 
    container.Inject(runConfiguration.Coordinator);
 
    Runner.Host(runConfiguration, args);
  }
}

 

Note this example uses StructureMap to push the IServiceCoordinator and ILogger dependencies into the instance of the health monitor created by Topshelf. This requires that once we have configured Topshelf we have to inject theIServiceCoordinator into the container otherwise it will not be found and StrucutreMap when creating theIHealthMonitoringService and an exception will be thrown.

 

You might be wondering where the ILogger comes from. I have a thin wrapper around Log4Net which I use in many applications so I have created a commons library. I’ve talked about it in more detail up on StackOverflow. The CommonsRegistry is in charge of setting up how ILoggers get created.

Show Me the Health Check

image

 

You can just as easily run this console application as a windows service and this same code will automatically stop the windows service. Obviously this application is not too useful on its own. It works better when you configure more than one service to be running at the same time. But that sounds like another blog post.