TopShelf结合QuartzNet构建windows服务

来源:互联网 发布:arma预测 matlab源码 编辑:程序博客网 时间:2024/06/10 13:01

一 、使用背景

我们在项目中经常会用到定时计划任务,传统的实现方法总结起来有三种:
  1. 通过winform的timer控件;
  2. 控制台+windows计划任务;
  3. 创建windows service。

但以上几种均可以实现简单的定时任务,但缺点也显而易见,前两种在执行期间需要客户端一直运行,而如果此时恰好有处女座登录服务器,搞不准就给干掉了;windows service相对就比较正规了,不过安装的繁琐和配置不够灵活也倒逼一好多懒人采用第2种方式。

那么有没有什么方法既能很方便地安装又能很灵活地配置任务呢,这就引出了本文的主旨。


二、框架简介

Topshelf是一个windows service框架,它对windows service进行封装使得我们可以把控制台程序安装成service,便捷的安装以及控制台可视化的调试大大提升了服务的开发效率。以上是我个人理解,如有错误,还望大侠纠正,不胜感激!

Quartz.Net是一款针对.net推出的开源作业调度框架,网上关于它的描述举不胜举,个人建议还是看英文原版网站:Quartz.NET - Documentation

先总结一下Quartz的三个概念:
- Schedule:任务调度器,这是一个抽象的概念,相当于整个框架的CPU,负责对Trigger和Job统筹执行;
- Trigger:job触发条件,比如每天固定时间执行、每隔多少时间执行等;
- Job:处理需要执行的任务,也就是我们代码要处理的具体业务。


三、代码实践

1.TopShelf搭建

(1)安装程序包:右键引用-打开nuget管理程序包,如下图,点击安装这里写图片描述

(2)安装完成即可自动完成对Topshelf.dll的引用

(3)控制台代码如下:

using Quartz;using Quartz.Impl;using Quartz.Simpl;using Quartz.Xml;using Topshelf;namespace JobSchedule{    class Program    {        static void Main(string[] args)        {            HostFactory.Run(x =>            {                x.Service<TownCrier>(s =>                {                    s.ConstructUsing(name => new TownCrier());                    s.WhenStarted(tc => tc.Start());                    s.WhenStopped(tc => tc.Stop());                });                x.RunAsLocalSystem();                x.SetDescription("QuartzNet任务调度服务,灵活配置任务计划");                x.SetDisplayName("QuartzJobShedule");                x.SetServiceName("Quartz任务调度框架");            });                   }    }    /// <summary>    /// 服务主体(英文原意:在城镇中沿街呼叫传报公告的人)    /// </summary>    public class TownCrier    {        private IScheduler scheduler;        public TownCrier()        {            //初始化相关配置        }        public void Start()        {            //启动服务              }        public void Stop()        {            //关闭服务                    }    }}

注释:主线程Main函数中调用HostFactory的Run方法,委托调用service,完成服务的初始化绑定,老司机可能会惊呼,这完全就是service创建的时候需要配置的东西嘛。这里需要注意ConstructUsing方法中的委托代码,这里实例化一个实例TownCrier类。TownCrier类中需要实现服务启动Start和服务关闭Stop方法,和windows服务的方法很相似,这里不再赘述。

2.引入QuartzNet实现任务调度

1.安装Quartz

引用quartz
管理Nuget程序包,安装后即多了Quartz.dll的引用。

2.更新TownCrier的代码如下

    public class TownCrier    {        private IScheduler scheduler;        public TownCrier()        {            //从配置中读取计划执行策略            XMLSchedulingDataProcessor processor = new XMLSchedulingDataProcessor(new SimpleTypeLoadHelper());            ISchedulerFactory sf = new StdSchedulerFactory();            scheduler = sf.GetScheduler();            processor.ProcessFileAndScheduleJobs("~/quartz_jobs.xml", scheduler);//quartz_jobs.xml文件路径        }        public void Start()        {             scheduler.Start();//启动quartz服务        }        public void Stop()        {                   scheduler.Shutdown(false);//true:等待正在运行的计划任务执行完毕;false:强制关闭        }    }

计划任务启动需要初始化schedule配置,可以放在构造中;然后再start和stop方法中启动和关闭quartz服务

3.计划任务实现类代码

using System;using Quartz;namespace JobSchedule{    public class TestJob: IJob    {        /// <summary>        /// 实现Ijob接口        /// </summary>        /// <param name="context"></param>        public void Execute(IJobExecutionContext context)        {           //job具体实现           Console.WriteLine("hello job");        }     } }

Execute方法中添加业务逻辑代码,建议从项目中分离,降低耦合。

4.quartz_jobs.xml配置文件

<?xml version="1.0" encoding="utf-8" ?><job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData">  <processing-directives>    <overwrite-existing-data>true</overwrite-existing-data>  </processing-directives>  <schedule>    <job>      <!--job名称-->      <name>JobName</name>      <!--job分组-->      <group>JobGroup</group>      <!--描述-->      <description>job描述</description>      <!--job方法所在命名空间.方法所在类,方法所在类-->      <job-type>JobSchedule.TestJob,TestJob</job-type>      <!--true:始终保留在Quartz的JobStore中;false:如果未关联trigger则移除-->      <durable>true</durable>      <recover>false</recover>    </job>    <!--复杂任务触发器-->    <trigger>      <cron>        <!--trigger名称-->        <name>TrigName</name>        <!--trigger分组-->        <group>TrigGroup</group>        <!--描述-->        <description>tirgger描述</description>        <!--绑定的job名称-->        <job-name>JobName</job-name>        <!--绑定的job组-->        <job-group>JobGroup</job-group>        <!--未按时触发时采取的策略-->        <misfire-instruction>SmartPolicy</misfire-instruction>        <!--每个整点触发-->        <cron-expression>0 0 0/1 * * ? </cron-expression><!--秒 分 时 天 月 (周) 年-->      </cron>          </trigger>  </schedule></job-scheduling-data>

配置文件包括两个大的节点job和trigger,各自描述自身的一些配置,注意两点:

  1. job-type必须指明job所在的程序集
  2. trigger中的job-name和job-group必须与需要调度的job中的name和group一致

cron-expression配置请点击cron-expression语法及帮助或者可以使用在线生成工具cron语法生成器


至此,我们已经搭建了一个简易的可以自由调度的任务框架,本篇重在动手实践,有一些原理讲解不到位还请指正批评。但人生的意义不就在于入门后的自我修行深入么?

原创粉丝点击