Страницы

воскресенье, 29 июля 2012 г.

Положи Windows Service на полку.

  Кому приходилось писать сервисы windows следуя заветам Microsoft осознаёт тяжесть (“корпоративность”) подхода. Долго, муторно, неудобно для тестирования, для отладки требуется ultimate версия студии. Возникает ощущение бега с препятствиями. Видимо схожие мысли посетили и авторов Mass Transit и Fubu Project. Они уже решили проблему создания сервисов, убрав все препятствия с трассы. Спешите освоить -  Topshelf.  

  Раздел документации на сайте не блещет полнотой, что поначалу несколько расстраивает. Хорошая новость: ее достаточно. Topshelf может работать в двух режимах:

  • installing - создание полноценной службы windows.
  • shelving – хостинг сервисов в отдельном домене приложения.

 

Installing

  Этот режим используется для создания полноценной службы windows, которой можно управлять через Service Manager. Идеологически Topshelf выполняет две операции:

  • Берёт на себя сложности связанные инфраструктурными проблемами хостинга windows-сервиса.
  • Хостинг бизнес-сервиса, выполняющего работу самого сервиса.

Эти две функции нам и надо прописать в коде:

static class Program
{
    static void Main()
    {
        HostFactory.Run(x =>
        {
            //Настройка windows-сервиса. 
            x.UseNLog();
            x.BeforeStartingServices(() => Console.WriteLine("[FooHost] Preparing to start host services"));
            x.AfterStartingServices(() => Console.WriteLine("[FooHost] All services have been started"));
            x.AfterStoppingServices(() => Console.WriteLine("[FooHost] All services have been stopped"));
            x.SetServiceName("FooHost");
            x.SetDisplayName("FooHost");
            x.SetDescription("FooHost ");
            //Сейчас запускаем как Local System, но можно запустить под любым пользователем. 
            x.RunAsLocalSystem();
            x.EnableDashboard();
            //Настройка нашего бизнес-сервиса.  
            x.Service<BarService>(y =>
            {
                y.SetServiceName("FooService");
                y.ConstructUsing(() => new FooService());
                y.WhenStarted(foo => foo.Start());
                y.WhenStopped(foo => foo.Stop());
            });
        });
        Logger.Shutdown();
    }
}

Не буду подробно останавливаться на коде, он говорит сам за себя. Добавлю: Topshelf поддерживает работу с двумя наиболее популярными логгерами – log4net и NLog2. Расширенное описание командной строки есть в исходнихах.


Преимущество режима  по сравнению с обычным сервисом:



  • Нет нужды наследовать и вручную писать ServiceInstaller и ServiceBase, что повышает тестопригодность.
  • Возможность запускать сервис в режиме консоли “из коробки”, что облегчает его отладку.
  • Внятное (даже без документации) API.

Создание нового windows-сервиса легко, но есть лучший способ хостить сервисы:


Shelving


В большинстве случаев нам не нужен полноценный windows-сервис, но мы вынуждены его писать, чтобы удовлетворить требования Windows. Отсюда вытекает логичная мысль: сделать инфраструктурный windows-сервис, который бы мог хостить бизнес-сервисы. Эту идею и реализует Topshelf.


Для начала нам понадобиться Topshelf.Host.exe. Его нет в nuget, но можно найти в исходниках проекта (я использую несколько модифицированную версию). Topshelf.Host.exe является таким же windows-сервисом, как и описанный в предыдущем разделе, и следовательно его можно запускать и как сервис, и как консоль. После запуска сервис автоматически создать подпапку Services. Topshelf мониторит эту папку, и при обнаружении изменений по необходимости подгружает


  Для дальнейшей работы необходимо реализовать Bootstrapper для своего сервиса, и сконфигурировать параметры запуска. У меня это выглядит так:

public class MazayBootstrapper : Bootstrapper<MazayService>
{
    public void InitializeHostedService(IServiceConfigurator<MazayService> cfg)
    {
        cfg.SetServiceName("Mazay");
        cfg.Named("Mazay");
        cfg.HowToBuildService(x => new MazayService(SectionHandler.Instance.Folders));
        cfg.SetServiceName("Mazay");
        cfg.WhenStarted(x => x.Start());
        cfg.WhenStopped(x => x.Stop());
    }
}

  После этого надо создать подпапку с названием сервиса в папке Service\FooService, и скопировать в нее наш сервис. Согласно описанию, сервис должен был заработать. Но заработал  только после продолжительных мытарств. Что еще раз напомнило мне о древней мудрости:  если ничего не помогает – прочитайте наконец документацию. Если и это не помогает – внимательно прочитайте документацию Улыбка Дело оказалось в двух проблемах:




  1. Добавить конфигурационный файл сервиса. Внимание: название файла должно соответствовать шаблону ‘имя_папки_сервиса.dll’, в которую мы положили сервис. Для моего случая это FooService.config, а не FooService.dll.config. Topshelf запускает сервис в отдельном домене приложения, и принудительно назначает ему этот конфигурационный файл.


  2. В конфигурационном файле необходимо указать ссылку на Bootstrapper сервиса:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section
      name="ShelfConfiguration"
      type="Topshelf.Shelving.ShelfConfiguration, TopShelf" />
  </configSections>
 
 <ShelfConfiguration
    Bootstrapper="Mazay.MazayBootstrapper, Mazay" />
</configuration>

После этого сервис заработал в штатном режиме.


Dashboard


Маленький бонус от создателей. Topshelf содержит небольшой проект Dashboard, который позволяет из браузера посмотреть, и управлять рабой сервисов. Для его подключения понадобиться nuget пакет Topshelf.DashBoard, и вписать строчку и вызвать метода расширения x.EnableDashboad() при конфигурации сервиса. Dashboard не поддаётся конфигурации, и найти его можно по адресу http://localhost:8483/TopShelf/Topshelf.Host


image


В общем, я остался очень доволен Topshelf, и в ближайшее время буду внедрять в своих проектах. Весьма рекомендую использовать всем.

Комментариев нет:

Отправить комментарий