65.9K
CodeProject 正在变化。 阅读更多。
Home

编写可扩展的轮询服务

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (3投票s)

2010年8月23日

CPOL

5分钟阅读

viewsIcon

30299

downloadIcon

417

带可配置插件任务的 Windows 轮询服务

引言

本文介绍了如何设计和开发一个 Windows 轮询服务,该服务足够灵活,可以支持未来添加新任务。

本文主要有两个部分:任务配置以及这些任务的创建和执行。

这同时也是一个很好的教程,介绍如何使用 Configuration Section Designer 设置 **自定义配置节**,以及如何使用系统计时器类在 Windows 服务中进行轮询。

背景

在企业系统环境中,您经常需要执行异步进程/任务。最常见的方法是创建一个 Windows 服务来轮询并执行这些任务。然而,随着时间的推移,这会导致创建大量的服务。

管理多个服务并确保它们都正常运行本身就是一项艰巨的任务。更不用说每次都需要重写相同的轮询服务代码所涉及的开发时间了。这促使我构建了一个通用的轮询服务,它可以以有序的方式运行任何任务,并且足够灵活,可以轻松地添加新任务。

必备组件

您需要从 CodePlex 下载并安装 **Configuration Section Designer**。

Using the Code

第一部分将介绍如何创建一个专门用于配置任务的配置节。Configuration Section Designer 工具是一个非常方便的小工具,可以轻松完成大部分繁重的工作,我强烈推荐它。

配置节

首先,我们需要创建一个配置节来处理单个任务的配置。

创建项目后,您需要在解决方案资源管理器中右键单击项目,然后选择“添加新项”。

这将打开“添加新项”对话框。对于 Configuration Section Designer 工具的默认安装,您应该能够选择 `ConfigurationSectionDesigner` 模板,如下图所示。

VsAddNewItem.JPG

设计配置节

打开设计器后,您需要通过从工具箱拖动“配置元素”图标来创建一个配置元素。右键单击设计器上新创建的配置元素,然后从上下文菜单中选择“属性”。然后,以与上面创建配置节相同的方式输入以下属性。

Name - Task
NameSpace - WSPolling

以与上面创建配置节相同的方式,将以下属性添加到 `Configuration` 元素。
* 请注意,XML 名称区分大小写。

名称 类型 XML 名称 IsKey
TaskName 字符串 taskName True
TaskOrder Int32 taskOrder
Enabled 布尔值 enabled

然后,我们需要以与上面相同的方式添加配置元素集合,您可以设置配置元素集合的属性。

Name - Tasks
Namespace - WSPolling
Xml Item Name - task 
Item Type - Task

然后,我们需要从工具箱添加一个配置节。以与定义配置元素属性相同的方式,右键单击设计器上新创建的配置节元素,然后从上下文菜单中选择“属性”。

在“属性”窗口中,设置以下属性:

Name - TaskConfiguration
Namespace - WSPolling
XML Section Name - taskConfiguration

* 请注意,XML 节名称区分大小写。

然后,您需要创建一个配置节下的元素并设置以下属性。

Name - Tasks
Type - Tasks 
Xml Name tasks

所有配置的最终结果应如下所示:

Configsection.JPG

以下代码创建了一个 Windows 轮询服务,该服务加载、排序并执行各种任务及其属性。

所需类

  • `ITask` – 定义单个任务的接口
  • `TaskManager` – 加载、排序和执行各种单个任务的对象
  • `Task1` - 任务的实现
  • `Task2` - 任务的实现

创建一个系统计时器

我们使用 `System.Timer` 类以固定的时间间隔触发事件来执行 Windows 服务的轮询操作。您可以配置计时器以在任何给定时间间隔触发 - 此处设置为 4 秒。首先,我们实例化一个 `TaskManager` 类型的对象。然后,通过查找 `app.config` 文件中的配置节来加载所有启用的任务。

public void runService()
{
TaskManager tm = new TaskManager();
tm.LoadSortedProviders();

aTimer = new System.Timers.Timer(4000);

this.aTimer.AutoReset = true;

// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(tm.ExecuteTasks);

aTimer.Start();
GC.KeepAlive(this.aTimer);
} 

TaskManager

此对象管理我们如何加载和执行任务。此对象包含两个主要方法。

`LoadSortedProviders` 方法加载 `TaskSettings` 配置节下的所有任务,然后按 `TaskOrder` 对它们进行排序。您可以在此方法中添加任何您可能需要的附加行为。

public void LoadSortedProviders()
{
try
{
foreach (Task tc in config.Tasks.OfType<Task>())
{
ITask t = Activator.CreateInstance(Type.GetType(tc.TaskName)) as ITask;
t.TaskOrder = tc.TaskOrder;
t.Enabled = tc.Enabled;
if (t.Enabled)
{
_cprovider.Add(t);
}
}
_cprovider.Sort(new TaskPriorityComparer());
}
catch (Exception ex)
{
}
}

`ExecuteTasks` 方法从排序的任务列表中执行每个任务。

public void ExecuteTasks(object source, ElapsedEventArgs e)
{
try
{
foreach (ITask t in _cprovider)
{
t.Execute();
}
}
catch (Exception ex)
{
}
}

Task1/Task 2

这些类包含实际的任务实现。您可以通过添加实现 `ITask` 接口的类,并在 `Task` 类的 `Execute` 方法中添加任务实现来扩展轮询服务。它们也需要在配置文件中进行配置,如下面的任务配置部分所述。

public void Execute( )
{
    Console.WriteLine("Executing task1");
}

任务配置

每个添加的任务都需要在配置文件中进行以下配置:
`taskName` 是您的 `Task` 对象的完全限定名称,包括命名空间;`enabled` 将决定在轮询服务启动时是否运行或忽略该任务;`taskOrder` 属性将决定任务的执行顺序。您可以根据您的自定义需求添加更多属性。

<tasks>
  <task taskName="WSPolling.Task1" enabled="true" taskOrder="0" />
  <task taskName="WSPolling.Task2" enabled="true" taskOrder="1" />
</tasks>

关注点

我想做的一件事是尝试让这些任务在它们自己的内存空间中运行,这样它们就不会产生相互依赖。也就是说,如果其中一个崩溃了,我想看到其他任务继续运行。

`System.Timer` 类将在从 `ThreadPool` 获取的线程上执行事件。

© . All rights reserved.