Что касается второй части вашего вопроса, я думал об этом, и вот класс, который я собрал, который помогает создать службу, которая может быть запущена как в виде консольного приложения, так и службы 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 у вас есть несколько вариантов
- Периодически звоните ИП с вашего сервиса и обрабатывайте напоминания
- Иметь задание SQL, которое периодически запускает SP и добавляет напоминание в очередь компонента Service Broker . Затем ваша служба может подписаться на очередь и обрабатывать напоминания по мере их появления в очереди. Преимущество этого подхода заключается в том, что вы можете масштабировать на нескольких серверах, обрабатывающих генерацию уведомлений-напоминаний, без какого-либо специального кодирования, просто добавьте другой сервер, на котором работает ваша служба, и сообщения будут автоматически распределены между двумя серверами.
Если вы не используете SQL Server для хранения напоминаний
Вы все еще можете использовать тот же подход, что и для SQL Server. Конечно, служба Windows может запрашивать хранилище данных, используя все, что угодно, и обрабатывать напоминания. Масштабировать его немного сложнее, поскольку вам нужно будет убедиться, что несколько серверов не обрабатывают одно и то же напоминание и т. Д., Но не разбивают поезда.
Я думаю, что это охватывает суть этого, все остальное является некоторой вариацией выше. В конечном итоге ваше решение будет зависеть от целевых объемов, требований к надежности и т. Д.