Справка Windows Service Design - PullRequest
       12

Справка Windows Service Design

1 голос
/ 07 сентября 2010

Я нахожусь в процессе разработки архитектуры приложения, которое планирую создать, и мне нужен совет по наилучшему способу реализации конкретного компонента службы Windows, описанному ниже. Я буду создавать сервис с использованием .net 4.0, чтобы воспользоваться преимуществами новых API-интерфейсов для параллельных вычислений и задач. Я также рассмотрел использование сервиса MSMQ, однако я не уверен, что это подходит для того, чего я надеюсь достичь.

Простейший способ объяснения моего варианта использования состоит в том, что пользователи могут создавать несколько напоминаний разных типов для задачи, которую они должны выполнить, которую они создают с помощью веб-приложения, встроенного в ASP.NET MVC 2. Эти Напоминания могут быть разных типов, например, электронная почта и SMS, причину которых необходимо отправить в указанное время. Напоминания можно изменять до тех пор, пока они не будут отправлены пользователю, приостановлены и отменены все вместе, что, я думаю, делает службу на основе очередей, такую ​​как MSMQ, неподходящей?

Я планирую разместить службу Windows, которая будет периодически (если нет более подходящего способа?) Проверять наличие каких-либо напоминаний, определять их тип и передавать их конкретному компоненту для обработки и отправки их. выкл. Если возникает исключение, напоминание будет поставлено в очередь с заданным интервалом и повторной попыткой, это будет продолжаться с увеличением интервала, пока они не достигнут установленного порогового значения, после чего они сбрасываются и регистрируются как сбой. Чтобы добавить последний уровень сложности к сервису, я надеюсь указать в файле конфигурации конкретную реализацию каждого типа (это означает, что я могу сказать, изменить сервис SMS из-за стоимости или чего-то еще), которые загружаются при запуске сервиса динамически. Любые напоминания о неизвестном или недоступном типе будут автоматически вызывать сбой и будут регистрироваться, как и прежде.

После того, как напоминание было успешно отправлено, оно просто отбрасывает его, однако с помощью шлюза SMS, который я планирую использовать, требуется, чтобы я вызвал его API, чтобы узнать, было ли сообщение успешно доставлено или нет, что означает дополнительное таймер требуется с установленным интервалом, чтобы проверить это. Также было бы неплохо иметь возможность добавлять дополнительные сервисы типа напоминания, которые соответствуют унифицированному интерфейсу, при запуске сервиса без необходимости изменять его код.

Наконец, я не знаю, следует ли публиковать это как отдельный вопрос или нет, но я подумал, можно ли сказать, что можно создать консольное приложение, которое можно запускать / останавливать в любое время и при запуске можно увидеть, какие окна сервис в настоящее время делает?

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

Заранее спасибо, Wayne

1 Ответ

4 голосов
/ 07 сентября 2010

Что касается второй части вашего вопроса, я думал об этом, и вот класс, который я собрал, который помогает создать службу, которая может быть запущена как в виде консольного приложения, так и службы Windows. Это только что появилось в прессе, поэтому могут возникнуть некоторые проблемы, требующие решения, и требуется некоторый рефакторинг. вокруг кода отражения.

NB: Вы должны установить тип вывода проекта службы на Console Application, он все равно будет нормально работать как обычный сервис.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;

namespace DotNetWarrior.ServiceProcess
{  
  public class ServiceManager
  {
    private List<ServiceBase> _services = new List<ServiceBase>();

    public void RegisterService(ServiceBase service)
    {
      if (service == null) throw new ArgumentNullException("service");
      _services.Add(service);
    }

    public void Start(string[] args)
    {
      if (Environment.UserInteractive)
      {
        foreach (ServiceBase service in _services)
        {
          Start(service, args);
        }
        Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
        Thread.Sleep(Timeout.Infinite);
      }
      else
      {
        ServiceBase.Run(_services.ToArray());
      }
    }    

    public void Stop()
    {
      foreach (ServiceBase service in _services)
      {
        Stop(service);
      }
    }

    private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
      Stop();
      Environment.Exit(0);
    }

    private void Start(ServiceBase service, string[] args)
    {
      try
      {
        Type serviceType = typeof(ServiceBase);       

        MethodInfo onStartMethod = serviceType.GetMethod(
          "OnStart", 
          BindingFlags.NonPublic | BindingFlags.Instance, 
          null, 
          new Type[] { typeof(string[]) }, 
          null);

        if (onStartMethod == null)
        {
          throw new Exception("Could not locate OnStart");
        }

        Console.WriteLine("Starting Service: {0}", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { args });
        Console.WriteLine("Started Service: {0}", service.ServiceName);
      }
      catch (Exception ex)
      {
        Console.WriteLine("Start Service Failed: {0} - {1}", service.ServiceName, ex.Message);
      }
    }

    private void Stop(ServiceBase service)
    {
      try
      {
        Type serviceType = typeof(ServiceBase);
        MethodInfo onStopMethod = serviceType.GetMethod(
          "OnStop", 
          BindingFlags.NonPublic | BindingFlags.Instance);
        if (onStopMethod == null)
        {
          throw new Exception("Could not locate OnStart");
        }
        Console.WriteLine("Stoping Service: {0}", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped Service: {0}", service.ServiceName);
      }
      catch (Exception ex)
      {
        Console.WriteLine("Stop Service Failed: {0} - {1}", service.ServiceName, ex.Message);
      }
    }
  }
}

Чтобы использовать это, вы можете скопировать стандартный код из Main точки входа службы и заменить его следующим:

static void Main(string[] args)
{
  ServiceManager services = new ServiceManager();
  services.RegisterService(new Service1());

  services.Start(args);      
}

Метод services.Start() обнаружит, что служба работает как интерактивное приложение, и вручную вызовет метод OnStart для всех зарегистрированных служб, после запуска основной поток перейдет в спящий режим. Чтобы остановить службы, просто нажмите «Ctrl + C», что приведет к остановке служб путем вызова метода OnStop службы.

Конечно, если SCM запускает приложение как Сервис, то все работает как обычный сервис. Единственное предостережение в том, что служба не должна запускаться с «Разрешить службе взаимодействовать с рабочим столом», поскольку это заставит службу работать в интерактивном режиме, даже если она запускается как служба. При необходимости это можно обойти, но я только что написал код.

Мониторинг и запуск / остановка службы

Из командной строки вы можете использовать NET.EXE для запуска / остановки службы

Start a service
net start <service name>

Stop a service
net stop <service name>

Для управления сервисом из .NET вы можете использовать System.ServiceProcess.ServiceController

// Stop a service
System.ServiceProcess.ServiceController sc = new
  System.ServiceProcess.ServiceController("<service name>");
sc.Stop();

Для общего взаимодействия со службой, отличной от той, которая предоставляется через ServiceController Я бы предложил разместить службу WCF как часть своей службы, которую затем можно использовать для связи со службой для запроса внутренних детали, специфичные для вашего сервиса.

Обработка планирования

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

Если вы используете SQL Server для хранения уведомлений.

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

С этим SP у вас есть несколько вариантов

  1. Периодически звоните ИП с вашего сервиса и обрабатывайте напоминания
  2. Иметь задание SQL, которое периодически запускает SP и добавляет напоминание в очередь компонента Service Broker . Затем ваша служба может подписаться на очередь и обрабатывать напоминания по мере их появления в очереди. Преимущество этого подхода заключается в том, что вы можете масштабировать на нескольких серверах, обрабатывающих генерацию уведомлений-напоминаний, без какого-либо специального кодирования, просто добавьте другой сервер, на котором работает ваша служба, и сообщения будут автоматически распределены между двумя серверами.

Если вы не используете SQL Server для хранения напоминаний

Вы все еще можете использовать тот же подход, что и для SQL Server. Конечно, служба Windows может запрашивать хранилище данных, используя все, что угодно, и обрабатывать напоминания. Масштабировать его немного сложнее, поскольку вам нужно будет убедиться, что несколько серверов не обрабатывают одно и то же напоминание и т. Д., Но не разбивают поезда.

Я думаю, что это охватывает суть этого, все остальное является некоторой вариацией выше. В конечном итоге ваше решение будет зависеть от целевых объемов, требований к надежности и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...