TopShelf结合QuartzNet构建windows服务

7 篇文章 0 订阅
2 篇文章 0 订阅

一 、使用背景

我们在项目中经常会用到定时计划任务,传统的实现方法总结起来有三种:
  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语法生成器


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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

by_ron

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值