当前位置: 首页 > 工具软件 > Topshelf > 使用案例 >

C#使用Topshelf和Quartz开发处理定时任务的Windows服务程序

袁炳
2023-12-01

背景

有些业务是运行在后台,需要界面,但需要开机就自动运行很适合以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,请自行下载。

C#代码示例

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的服务管理面板中对服务进行启动和停止操作,当然也可以设置为开机启动和禁用。

 类似资料: