Quartz.NET开源库实现作业定时调度——简单示例

一、Quartz.NET简介

1.1、.NET Framework自带的Timer定时器介绍

    很多的软件项目中都会使用到定时任务、定时轮询数据库同步,定时邮件通知等功能。.NET Framework具有“内置”定时器功能,通过System.Timers.Timer类实现

自带定时器的缺点:

①在使用Timer类需要面对的问题:计时器没有持久化机制;

②计时器具有不灵活的计划(仅能设置开始时间和重复间隔,没有基于日期,时间等);

③计时器不使用线程池(每个定时器一个线程);计时器没有真正的管理方案 - 你必须编写自己的机制,以便能够记住,组织和检索任务的名称等。

1.2、Quartz.NET介绍

    Quartz.NET是一个强大、开源、轻量的作业调度框架,可以与任何其他软件系统集成或一起使用。作业调度程序是一个系统,负责在执行预处理程序时执行(或通知)其他软件组件 - 确定(调度)时间到达。Quartz是非常灵活的,并且包含多个使用范例,可以单独使用或一起使用,以实现您所需的行为,并使您能够以您的项目看起来最“自然”的方式编写代码。

Quartz.NET优点:

①组件的使用非常轻便,并且需要非常少的设置/配置,如果您的需求相对基础,它实际上可以使用“开箱即用。

②是容错的,并且可以在系统重新启动之间保留(记住)您的预定作业。

能够用它来为执行一个作业而创建简单的或复杂的作业调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等。非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。

    Quartz是作为一个小的动态链接库(.dll文件)分发的,它包含所有的核心Quartz功能。 此功能的主要接口(API)是调度程序接口。 它提供简单的操作,如调度/非调度作业,启动/停止/暂停调度程序。如果你想安排你自己的软件组件执行,他们必须实现简单的Job接口,它包含方法execute()。 如果希望在计划的触发时间到达时通知组件,则组件应实现TriggerListener或JobListener接口。主要的Quartz'进程'可以在您自己的应用程序或独立应用程序(使用远程接口)中启动和运行。

1.3、Quarzt.NET的官方地址

官网:http://www.quartz-scheduler.net/

源码:https://github.com/quartznet/quartznet

示例:Quartz.NET Quick Start Guide | Quartz.NET (quartz-scheduler.net)

Quartz.NET是 OpenSymphony 的 Quartz API 的.NET移植,用C#改写,可用于winform和Web应用中。它灵活而不复杂,你能够用它来为执行一个作业而创建简单的或复杂的作业调度。Quartz.NET 3.0 已经开始支持 .NET Core/.NET Standard 2.0。
Quartz.NET开源库实现作业定时调度——简单示例

    Job 为作业的接口,JobDetail 用来描述 Job 的实现类及其它相关的静态信息;Trigger 作为作业的定时管理工具。

    一个 Trigger 只能对应一个作业实例,而一个作业实例可对应多个 Trigger ;

    Scheduler 做为定时任务容器,它包含了所有触发器和作业,每个 Scheduler 都存有 JobDetail 和 Trigger的注册,一个 Scheduler 中可以注册多个 JobDetail 和多个 Trigger 。 

二、实现步骤

2.1、安装Quartz.NET包

Quartz.NET开源库实现作业定时调度——简单示例

Quartz.NET开源库实现作业定时调度——简单示例

Quartz.NET开源库实现作业定时调度——简单示例

2.2、创建一个简单的定时任务示例

这里实现一个定时执行方法输出执行时间的示例

1、编写需要定时执行的方法,必须继承【IJob】

/***
*	Title:"数据采集" 项目
*		主题:定时任务
*	Description:
*		功能:XXX
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/

using Quartz;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace TestQuartz.FixedTimeTask
{
    class TimeJob : IJob
    {
        /// <summary>
        /// 作业调度定时执行的方法
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Execute(IJobExecutionContext context)
        {
            string str = $"{this.GetType().Name} 方法执行,执行时间是:{DateTime.Now} ID:{context.FireInstanceId}" +Environment.NewLine;
            await Console.Out.WriteLineAsync(str);
        }

    }//Class_end

}

2、要定时执行这个方法,你需要先用【IJobDetail】接口绑定该方法,然后再使用【ITrriger】接口监听,接着使用【StdSchedulerFactory】调度工厂实例化【IScheduler】调度器接口启动;

//1、使用IJobDetail 接口绑定需要定时执行的方法【TimeJob】
IJobDetail job = JobBuilder.Create<TimeJob>()
	.WithIdentity("job1", "group1")
	.Build();

// 2、使用ITrriger监听器每10秒重复监听
ITrigger trigger = TriggerBuilder.Create()
	.WithIdentity("trigger1", "group1")
	.StartNow()
	.WithSimpleSchedule(x => x
		.WithIntervalInSeconds(10)
		.RepeatForever())
	.Build();

// 3、通知调度器用定义好的监听器执行该绑定的方法
await scheduler.ScheduleJob(job, trigger);

// 注意:你也可以让调度器使用多个监听器监听同一个方法
// await scheduler.ScheduleJob(job, new List<ITrigger>() { trigger1, trigger2 }, replace: true);

3、确定任务执行的时间间隔

//任务执行的时间间隔(可自定义,比如这里的是10秒)
await Task.Delay(TimeSpan.FromSeconds(10));

4、还可以添加日志记录,不过当没有任何日志接口对接时,该日志则不进行记录。

//日志类需要继承ILogProvider接口
private class ConsoleLogProvider : ILogProvider
{
    public Logger GetLogger(string name)
    {
        return (level, func, exception, parameters) =>
        {
            if (level >= LogLevel.Info && func != null)
            {
                Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
            }
            return true;
        };
    }

    public IDisposable OpenNestedContext(string message)
    {
        throw new NotImplementedException();
    }

    public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
    {
        throw new NotImplementedException();
    }
}

//调用方法
LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());

2.3、完整的调用示例

using System;
using System.Threading.Tasks;

using Quartz;
using Quartz.Impl;
using Quartz.Logging;

namespace QuartzSampleApp
{
    public class Program
    {
        private static async Task Main(string[] args)
        {
            LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());

            // Grab the Scheduler instance from the Factory
            StdSchedulerFactory factory = new StdSchedulerFactory();
            IScheduler scheduler = await factory.GetScheduler();

            // and start it off
            await scheduler.Start();

            // define the job and tie it to our HelloJob class
            IJobDetail job = JobBuilder.Create<HelloJob>()
                .WithIdentity("job1", "group1")
                .Build();

            // Trigger the job to run now, and then repeat every 10 seconds
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("trigger1", "group1")
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(10)
                    .RepeatForever())
                .Build();

            // Tell quartz to schedule the job using our trigger
            await scheduler.ScheduleJob(job, trigger);

            // some sleep to show what's happening
            await Task.Delay(TimeSpan.FromSeconds(60));

            // and last shut down the scheduler when you are ready to close your program
            await scheduler.Shutdown();

            Console.WriteLine("Press any key to close the application");
            Console.ReadKey();
        }

        // simple log provider to get something to the console
        private class ConsoleLogProvider : ILogProvider
        {
            public Logger GetLogger(string name)
            {
                return (level, func, exception, parameters) =>
                {
                    if (level >= LogLevel.Info && func != null)
                    {
                        Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
                    }
                    return true;
                };
            }

            public IDisposable OpenNestedContext(string message)
            {
                throw new NotImplementedException();
            }

            public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
            {
                throw new NotImplementedException();
            }
        }
    }

    public class TimeJob: IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
           string str = $"{this.GetType().Name} 方法执行,执行时间是:{DateTime.Now} ID:{context.FireInstanceId}" +Environment.NewLine;
            await Console.Out.WriteLineAsync(str);
        }
    }
}

三、封装好了一个简易的单个任务调度器

3.1、单个定时任务调度器的核心

/***
*	Title:"数据采集" 项目
*		主题:单个任务调度器
*	Description:
*		功能:
*		    1、启动定时方法
*		    2、关闭定时方法
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/

using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace FixedTimeTask
{
    class SingleTaskScheduler
    {
        #region   基础参数
        //调度工厂
        private static StdSchedulerFactory _factory;
        //调度器
        private static IScheduler _scheduler;


        //详细任务
        private IJobDetail _job;
        //触发器
        private ITrigger _trigger;

        //触发的时间间隔
        private int _trrigerInterval = 5;

        //任务执行的时间间隔
        private int _taskRunInterval = 20;

        //执行次数
        private uint _runNumber = 0;

        #endregion


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="taskRunInterval">任务执行的时间间隔</param>
        /// <param name="trrigerInterval">触发器的时间间隔</param>
        public SingleTaskScheduler(int taskRunInterval,int trrigerInterval)
        {
            _taskRunInterval = taskRunInterval;
            _trrigerInterval = trrigerInterval;
        }



        #region   公有方法

        /// <summary>
        /// 启动定时方法
        /// </summary>
        /// <returns></returns>
        public async Task StartFiexedTimeMethod<T>()where T:IJob
        {
            Console.WriteLine("开始调用定时任务!!!");
            //创建日志
            LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());

            //从调度工厂获取调度器实例
            _factory = new StdSchedulerFactory();
            _scheduler = await _factory.GetScheduler();

            //启动实例
            await _scheduler.Start();

            //绑定定时执行方法
            BindFiexedTimeMethod<T>();

            //创建定时执行的触发器
            CreateFiexedTimeTrigger(_trrigerInterval);

            //告诉quartz用我们的触发器安排任务
            await _scheduler.ScheduleJob(_job,_trigger);

            //任务执行的时间间隔
            await Task.Delay(TimeSpan.FromSeconds(_taskRunInterval));

            Console.WriteLine("------------执行完成一轮定时任务----------");
        }

        /// <summary>
        /// 关闭定时方法
        /// </summary>
        /// <returns>返回关闭结果</returns>
        public bool CloseFiexedTimeMethod()
        {
            Task task=_scheduler?.Shutdown();

            return task.IsCompletedSuccessfully;
        }

        #endregion



        #region   私有方法
        /// <summary>
        /// 绑定定时方法
        /// </summary>
        private void BindFiexedTimeMethod<T>()where T:IJob
        {
             _job = JobBuilder.Create<T>()
                //.WithIdentity("时间任务1", "组1")
                .Build();


        }

        /// <summary>
        /// 创建定时执行触发器
        /// </summary>
        /// <param name="seconds"></param>
        private void CreateFiexedTimeTrigger(int seconds)
        {
            if (seconds < 0) return;

            _trigger = TriggerBuilder.Create()
             //.WithIdentity("时间任务触发器1", "组1")
                .StartNow()
                .WithSimpleSchedule(x => x
                .WithIntervalInSeconds(seconds)
                .RepeatForever())
                .Build();
        }


        #endregion 

    }//Class_end

}

3.2、单定时任务调度器使用方法

//1-引用命名空间
using FixedTimeTask;

//2-实例化单定时任务调度器(可以*定义:任务执行的时间间隔、与触发器执行的时间间隔)
SingleTaskScheduler taskScheduler = new SingleTaskScheduler(10,5);

//3-启动定时方法

await taskScheduler.StartFiexedTimeMethod<TimeJob>();

//4-最后可以关闭定时方法
bool success = taskScheduler.CloseFiexedTimeMethod();
上一篇:2021-09-17 Hadoop中Yarn容量资源调度器多队列提交案例


下一篇:@Scheduled并行执行