有些业务是运行在后台,需要界面,但需要开机就自动运行很适合以Windows服务的形式部署,在.net领域为了简化Windows服务的开发最为常用的是使用Topshelf框架。简直就是傻瓜式的就可以把一个控制台程序转为一个Windows服务程序,而且服务的安装、启动、停止、卸载还非常简单。而Windows服务程序中实现的大多数业务都是持续定时触发的。通常就是任务调度程序,而业界最流行的任务调度框架就是Quartz,无论是java还是.net此框架都是实现任务调度的不二之选。本文就重点介绍一下这个框架的配合使用开发处理定时任务的Windows服务。
1、TopShelf:需要依赖Topshelf.dll,请自行下载。
2、Quartz:需要依赖Common.Logging.Core.dll、Common.Logging.dll、Quartz.dll,请自行下载。
1、任务实现代码,就是定时任务要执行的代码,任务类要实现Quartz.IJob接口的Execute方法。
在解决方案中新建一个类库项目,命名为Wongoing.Jobs,并添加对Quartz.dll的引用,在此项目中添加一个类ServiceMonitorJob.cs,如下:
using System;
using System.Collections.Generic;
using System.Text;
using Quartz;
namespace Wongoing.Jobs
{
/// <summary>
/// 服务监控任务类,主要通过心跳完成服务运行状态的监控
/// </summary>
public class ServiceMonitorJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("服务监控...");
}
}
}
2、Windows服务实现代码。Windows服务类要实现Topshelf.ServiceControl和Topshelf.ServiceSuspend接口的方法。
在解决方案下,新建一个控制台项目,命名为BTSDataService,设为启动项目,并在控制台项目中添加对Wongoing.Jobs项目、Topshelf.dll、Common.Logging.Core.dll、Common.Logging.dll、Quartz.dll的引用。然后在控制项目中添加一个类ServiceRunner.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using Topshelf;
using Quartz;
using Quartz.Impl;
namespace BTSDataService
{
public sealed class ServiceRunner : ServiceControl, ServiceSuspend
{
private readonly IScheduler scheduler; //定义任务调度器
/// <summary>
/// 构造方法
/// </summary>
public ServiceRunner()
{
scheduler = StdSchedulerFactory.GetDefaultScheduler(); //从任务调度工厂获取默认的任务调度器
}
/// <summary>
/// 服务启动
/// </summary>
/// <param name="hostControl"></param>
/// <returns></returns>
public bool Start(HostControl hostControl)
{
try
{
this.scheduler.Start();
Console.WriteLine("服务已启动...");
return true;
}
catch(Exception ex)
{
Console.WriteLine("服务启动失败:" + ex.Message);
return false;
}
}
/// <summary>
/// 服务继续
/// </summary>
/// <param name="hostControl"></param>
/// <returns></returns>
public bool Continue(HostControl hostControl)
{
try
{
this.scheduler.ResumeAll();
Console.WriteLine("服务已继续...");
return true;
}
catch (Exception ex)
{
Console.WriteLine("服务继续失败:" + ex.Message);
return false;
}
}
/// <summary>
/// 服务暂停
/// </summary>
/// <param name="hostControl"></param>
/// <returns></returns>
public bool Pause(HostControl hostControl)
{
try
{
this.scheduler.PauseAll();
Console.WriteLine("服务已暂停...");
return true;
}
catch (Exception ex)
{
Console.WriteLine("服务暂停失败:" + ex.Message);
return false;
}
}
/// <summary>
/// 服务停止
/// </summary>
/// <param name="hostControl"></param>
/// <returns></returns>
public bool Stop(HostControl hostControl)
{
try
{
this.scheduler.Shutdown(false);
Console.WriteLine("服务已停止...");
return true;
}
catch (Exception ex)
{
Console.WriteLine("服务停止失败:" + ex.Message);
return false;
}
}
}
}
3、完善程序入口
修改启动项目(即控制台项目)的Program.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Topshelf;
using Topshelf.HostConfigurators;
namespace BTSDataService
{
class Program
{
static void Main(string[] args)
{
Action<HostConfigurator> d = new Action<HostConfigurator>(ActionMethod);
HostFactory.Run(d);
}
}
public static void ActionMethod(HostConfigurator hc)
{
hc.Service<ServiceRunner>();
hc.SetDescription("我的服务描述"); //windows服务描述
hc.SetDisplayName("MyServiceDisplayName"); //windows服务显示的名称
hc.SetServiceName("MyServiceName"); //windows服务的名称
hc.EnablePauseAndContinue();
}
}
在启动项目(控制台项目)下增加2个配置文件,并把[复制到输出目录]属性设置为"如果较新则复制"。
1、第一个配置文件为quartz.config,内容如下:
# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence
quartz.scheduler.instanceName = QuartzTest
# configure thread pool info
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount = 2
quartz.threadPool.threadPriority = Normal
# job initialization plugin handles our xml reading, without it defaults are used
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
quartz.plugin.xml.fileNames = ~/quartz_jobs.xml
# export this server to remoting context
#quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
#quartz.scheduler.exporter.port = 555
#quartz.scheduler.exporter.bindName = QuartzScheduler
#quartz.scheduler.exporter.channelType = tcp
#quartz.scheduler.exporter.channelName = httpQuartz
你可以通过修改quartz.threadPool.threadCount = 2中的2改变处理定时任务的线程数。
2、第2个配置文件为quartz_jobs.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file contains job definitions in schema version 2.0 format -->
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<!--调度任务配置-->
<job>
<name>ServiceMonitorJob</name>
<group>Monitor</group>
<description>服务监控</description>
<job-type>Wongoing.Jobs.ServiceMonitorJob,Wongoing.Jobs</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<!--任务触发配置-->
<trigger>
<cron>
<name>ServiceMonitorJob</name>
<group>Monitor</group>
<job-name>ServiceMonitorJob</job-name>
<job-group>Monitor</job-group>
<start-time>2019-07-01T00:00:00+08:00</start-time>
<cron-expression>0/3 * * * * ?</cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
可以通过修改0/3 * * * * ?改变触发任务的时机,0/3 * * * * ?代表每3秒触发一次。具体表达式的写法请学习cron表达式。
这时就可以启动此控制台程序,就会看到控制台先输出“服务已启动…”,然后每3秒输出一条“服务监控…”,按Ctrol+C停止服务并退出。
以Release方式编译控制台项目,把Release输出目录下的内容复制到一个指定目录,比如d:\myservice下。以管理员方式运行cmd。通过以下命令进行服务的安装、启动、停止、卸载。
1、服务安装
d:\myservice\BTSDataService.exe install
2、服务启动
d:\myservice\BTSDataService.exe start
3、服务停止
d:\myservice\BTSDataService.exe stop
4、服务卸载
d:\myservice\BTSDataService.exe uninstall
服务安装后,就可以在Windows的服务列表中看到此服务,也可以在windows的服务管理面板中对服务进行启动和停止操作,当然也可以设置为开机启动和禁用。