Windows服务设计帮助

时间:2010-09-06 22:30:20

标签: c# .net windows-services

我正在设计我正计划构建的应用程序的体系结构,并且需要一些关于实现下面描述的特定Windows服务组件的最佳方法的建议。我将使用.net 4.0构建服务,以便我可以利用新的并行和任务API,我也看过使用MSMQ服务但是我不确定这是否适合我希望实现的目标。

解释我的用例的最简单方法是,用户可以为他们需要完成的任务创建许多不同类型的提醒,他们使用ASP.NET MVC 2中构建的基于Web的应用程序创建这些提醒。提醒可以是各种类型,例如电子邮件和SMS,其原因必须在指定的截止时间发送。提醒可以更改,直到他们被发送给用户,暂停和取消所有这一点,我想这使得基于排队的服务,如MSMQ不合适?

我计划定期托管一个Windows服务(除非有更合适的方式?)检查是否有任何提醒到期,确定其类型并将它们传递给特定组件以处理它们并发送它们关闭。如果发生异常,提醒将按设定的间隔排队并再次尝试,这将继续发生,间隔增加,直到它们达到设定的阈值,此时它们将被丢弃并记录为失败。为了向服务添加最后一层复杂性,我希望在配置文件中指定每种类型的具体实现(这意味着我可以说由于成本或其他原因而更改SMS服务),这些服务在服务启动时加载动态。任何未知或不可用类型的提醒都会自动失败并像以前一样记录。

一旦成功发送了提醒,它就会丢弃它,但是对于我打算使用的SMS网关,它需要我调用它的API来查明消息是否成功传递,这意味着另外一个设定的间隔需要定时器来检查。能够在服务启动时添加符合统一接口的其他提醒类型服务而不需要更改其代码也是很好的。

最后,我不知道这是否应该作为一个单独的问题发布,但我想知道是否可以说构建一个可以随时启动/停止的控制台应用程序,当运行时可以看到窗口是什么服务目前在做什么?

这是我有关Stackoverflow的第一个问题,即使我已经使用社区一段时间了,所以如果我做错了,我会道歉。

提前致谢, 韦恩

1 个答案:

答案 0 :(得分:4)

对于你的问题的第二部分,我一直在考虑这个问题,这是我放在一起的一个类,它有助于创建一个既可以作为控制台应用程序也可以作为Windows服务运行的服务。这是新闻,所以可能有一些问题需要解决,一些重构需要esp。反思代码周围。

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. 定期从您的服务中致电SP并处理提醒
  2. 拥有一个定期运行SP的SQL作业,并向Service Broker Queue添加提醒。然后,您的服务可以订阅队列并处理队列中显示的提醒。这种方法的优点是,您可以使用多个服务器进行扩展,无需任何特殊编码即可生成提醒通知,只需添加另一台运行服务的服务器,消息将自动在两台服务器之间分发。
  3. 如果您没有使用SQL Server存储提醒

    您仍然可以使用与SQL Server类似的方法。当然,Windows服务可以使用合适的内容查询数据存储并处理提醒。由于您需要确保多个服务器不处理相同的提醒等但不是火车粉碎,因此要进行此扩展会有点困难。

    我认为这涵盖了它的要点,其他一切都是上面的一些变化。最终,您的决定将取决于目标量,可靠性要求等。