Кому приходилось писать сервисы 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, и скопировать в нее наш сервис. Согласно описанию, сервис должен был заработать. Но заработал только после продолжительных мытарств. Что еще раз напомнило мне о древней мудрости: если ничего не помогает – прочитайте наконец документацию. Если и это не помогает – внимательно прочитайте документацию Дело оказалось в двух проблемах:
- Добавить конфигурационный файл сервиса. Внимание: название файла должно соответствовать шаблону ‘имя_папки_сервиса.dll’, в которую мы положили сервис. Для моего случая это FooService.config, а не FooService.dll.config. Topshelf запускает сервис в отдельном домене приложения, и принудительно назначает ему этот конфигурационный файл.
- В конфигурационном файле необходимо указать ссылку на Bootstrapper сервиса:
<?xml version="1.0" encoding="utf-8"?><configuration><configSections><sectionname="ShelfConfiguration"type="Topshelf.Shelving.ShelfConfiguration, TopShelf" /></configSections><ShelfConfigurationBootstrapper="Mazay.MazayBootstrapper, Mazay" /></configuration>
После этого сервис заработал в штатном режиме.
Dashboard
Маленький бонус от создателей. Topshelf содержит небольшой проект Dashboard, который позволяет из браузера посмотреть, и управлять рабой сервисов. Для его подключения понадобиться nuget пакет Topshelf.DashBoard, и вписать строчку и вызвать метода расширения x.EnableDashboad() при конфигурации сервиса. Dashboard не поддаётся конфигурации, и найти его можно по адресу http://localhost:8483/TopShelf/Topshelf.Host
В общем, я остался очень доволен Topshelf, и в ближайшее время буду внедрять в своих проектах. Весьма рекомендую использовать всем.