Как стать автором
Обновить

Полноценный Windows Service с настраиваемым расписанием за 30 строк

Время на прочтение5 мин
Количество просмотров1.9K
Привет всем.

Недавно решился таки прочитать всю серию статей про Data acquisition. И начав с первой части нашёл для себя много интересного про windows service.
Как раз висела задача написать простой сервис, который будет по расписанию дергать наш корпоративный сайт. Думаю, вот он шанс закрепить свежие знания. Честно начал прикручивать все фишки, о которых было написано в статье касательно windows service, но начал натыкаться на косяки. Но не о косяках речь.
Мне пришла в голову другая мысль, что наверняка кто-то уже делал такие умные сервисы, и наверняка есть готовые красивые решения. Пара минут поиска и каково было удивление, когда я нашел проект NCron, который дает не только легкий способ создания сервиса, но также:
  1. Умеет сам себя инсталлировать и деинсталлировать как windows service.
  2. Может выполнять задачи по гибкому расписанию (от простого, каждый день в 6 часов утра, до более сложных, каждого третьего числа квартального месяца в 18 часов 40 минут)
  3. Позволяет настроить неограниченное количество задач для выполнения.
  4. Позволяет легко прикрутить логирование распространенных фреймворков и имеет уже свой простой встроенный механизм логирования в Event Log.
  5. Имеет достаточно гибкости, чтобы можно было пользоваться любимыми IoC контейнерами

И еще кое-чего полезного и удобного. Итак не сложно заметить, что это было как раз то, что мне нужно. Делает все практически без телодвижений с моей стороны. Более того скажу, что сервисы очень часто и пишутся для таких вот простых задач, которые этот проект и покрывает с головой.


Итак как же прикрутить сие чудо.

Создаем проект



Вначале создаем проект Console application. Тут все стандартно и просто.

Картинка 1

Добавляем Reference на NCron



Картинка 3

Слегка меняем main метод



using NCron.Service;
using NCron.Fluent.Crontab;
using NCron.Fluent.Generics;

namespace ScheduledService
{
  class Program
  {
    static void Main(string[] args)
    {
      Bootstrap.Init(args, ServiceSetup);
    }

    static void ServiceSetup(SchedulingService service)
    {
    }
  }
}


Тут может возникнуть вопрос, зачем передаются аргументы командной строки, нужны они для того, чтобы в последствии сервис мог сам себя инсталлировать (нужно будет всего лишь запустить его с параметром install). Метод ServiceSetup понадобится позже для настройки наших задач по расписанию.

Создаём простой таск



Делается это просто. Унаследовав свой класс от класса CronJob и переопределив абстрактный метод Execute. Я добавил сразу упрощенную реализацию запроса по какой-то линке, которая приходит в конструктор нашей задачи.

using NCron;

namespace ScheduledService
{
  public class RequestParameters
  {
    public String Uri { get; set; }
    public int Timeout { get; set; }
  }

  public class RequestSenderJob : CronJob
  {
    private RequestParameters RequestParameters { get; set; }
    public RequestSenderJob(RequestParameters requestParameters)
    {
      RequestParameters = requestParameters;
    }

    public override void Execute()
    {
      WebRequest request = WebRequest.Create(RequestParameters.Uri);
      request.UseDefaultCredentials = true;
      request.Timeout = RequestParameters.Timeout > 0 ? RequestParameters.Timeout * 1000 : 60*1000;//default 1 min<br/>
      HttpWebResponse response = (HttpWebResponse)request.GetResponse();
      response.Close();
    }
  }
}


Надо сказать, что в случае ошибки, её описание уже будет логироваться в Event Log, и при необходимости можно подключить свою любимую библиотеку для логирования.
Теперь таск создан и нужно только настроить его запуск.

Настройка запуска задачи



Здесь и пригодится созданный ранее метод ServiceSetup, в который мы и впишем логику по формированию очереди вызовов

using NCron.Fluent.Crontab;
using NCron.Service;

namespace ScheduledService
{
  class Program
  {
    static void Main(string[] args)
    {
      Bootstrap.Init(args, ServiceSetup);
    }

    static void ServiceSetup(SchedulingService service)
    {
      XDocument document = XDocument.Load("Config.xml");
      var requestTasks = from task in document.Root.Elements("RequestTask") select task;

      foreach (XElement requestTask in requestTasks)
      {
        RequestParameters requestParameters = new RequestParameters
                             {
                               Uri = requestTask.Attribute("RequestUri").Value,
                               Timeout = int.Parse(requestTask.Attribute("Timeout").Value)
                             };
        String shedule = requestTask.Attribute("Shedule").Value;
        service.At(shedule).Run(() => new RequestSenderJob(requestParameters));
      }
    }

  }
}



Для xml файла вида

<?xml version="1.0" encoding="utf-8" ?>
<Config>
  <RequestTask RequestUri="http://habrahabr.ru/" Shedule="0 1 * * *" Timeout="180"/>
</Config>


Да тут можно попинать меня за то, что я не проверил на null ни одной переменной (делал исключительно для наглядности и простоты примера). Но заинтересовать вас должна переменная shedule. В ней будет хранится строка описывающее расписание, по которому запускается таск.

Настройка расписания



Расписание, по которому запускается таск задается с помощью строки формата MINUTES HOURS DAYS MONTHS DAYS-OF-WEEK.
Вот пару примеров с самого сайта:
// Каждое воскресение в 9 утра.<br/>
service.At("0 9 * * 0").Run<EatBaconAndEggs>();

// В 8 часов утра, в каждое первое число квартала.<br/>
service.At("0 8 1 1,4,7,10 *").Run<SubmitVatDeclaration>();

// Каждые пол часа, в рабочее время с 8 до 17, в рабочие дни недели.<br/>
service.At("*/30 8-17 * * 1-5").Run<LookAtStackoverflow>();



Как видно из примера задавать можно вместо чисел: '*' — выбрать все, 'n-m' — промежуток от m до n, '*/n' — каждое n-ое значение, ну и на десерт можно перечислять значения через запятую и группировать другие виды выборок, да и вообще можно программно задать свои правила для расписания.

Установка



Ну и на последок о том как запустить наш сервис, в котором к слову ни одного класса связанного с сервисом мы и не видели. Для этого я написал 2 элементарных батника.

install.bat
ScheduledService install
pause



uninstall.bat
ScheduledService uninstall
pause



Есть правда и более продвинутая версия с параметрами, чтобы сервис сразу создался с нужными настройками. Об этом можете почитать здесь.

Вот в принципе и все.
Теги:
Хабы:
+5
Комментарии5

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн