Quartz.NET 作业调度

Quartz 简介:

Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等

Quartz.NET允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。

整合了 Quartz.NET的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。

使用Quartz

       一旦调度程序被实例化,它就可以启动,处于待机模式并关闭。请注意,一旦调度程序关闭,就不能在不重新实例化的情况下重新启动它。在调度程序启动之前,触发器不会触发(作业不执行),也不会在处于暂停状态时触发。

简单案例:

using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleQuartz
{
    class Program
    {
        static void Main(string[] args)
        {
            // trigger async evaluation
            RunProgramRunExample().GetAwaiter().GetResult();

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


        private static async Task RunProgramRunExample()
        {
            try
            {
                // Grab the Scheduler instance from the Factory
                NameValueCollection props = new NameValueCollection
                {
                    { "quartz.serializer.type", "binary" }
                };
                StdSchedulerFactory factory = new StdSchedulerFactory(props);
                // get a scheduler
                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();
            }
            catch (SchedulerException se)
            {
                Console.WriteLine(se);
            }
        }
    }
}
public class HelloJob : IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        await Console.Out.WriteLineAsync("HelloJob is executing.");
    }
}

Quartz API的关键接口和类是:

  • IScheduler - 与调度程序交互的主要API。
  • IJob - 由您希望由调度程序执行的组件实现的接口。
  • IJobDetail - 用于定义Jobs的实例。
  • ITrigger - 一个组件,用于定义执行给定作业的计划。
  • JobBuilder - 用于定义/构建JobDetail实例,用于定义Jobs的实例。
  • TriggerBuilder - 用于定义/构建触发器实例。

工作和触发器

Job是一个实现IJob接口的类,它只有一个简单的方法:

IJob接口

    namespace Quartz
    {
        public interface IJob
        {
            Task Execute(JobExecutionContext context);
        }
    }

 

身份标识:

     作业和触发器在Quartz 调度程序注册时被赋予标识。作业和触发器的键JobKey和TriggerKey)允许将它们放入“组”中。

     作业或触发器的键的名称部分在组内必须是唯一的。

  • 换句话说,作业或触发器的完整键(或标识符)是名称和组的组合。

如何为Job实例提供属性/配置 或 如何在执行之间跟踪作业的状态?

通过JobDataMap ,它是JobDetail对象的一部分。

JobDataMap 简介:

JobDataMap 可用于保存在Job实例执行时,可用的任意数量(可序列化)对象。JobDataMap是IDictionary接口的一个实现,并且具有一些用于存储和检索基本类型数据的便利方法。

JobDataMap中设置值

// define the job and tie it to our DumbJob classI
JobDetail job = JobBuilder.Create<DumbJob>()
.WithIdentity("myJob", "group1") // name "myJob", group "group1" .UsingJobData("jobSays", "Hello World!") .UsingJobData("myFloatValue", 3.141f) .Build();

Job执行期间 JobDataMap获取值

public class DumbJob : IJob{
    public async Task Execute(IJobExecutionContext context)
    {
        JobKey key = context.JobDetail.Key;
        JobDataMap dataMap = context.JobDetail.JobDataMap;
        string jobSays = dataMap.GetString("jobSays");
        float myFloatValue = dataMap.GetFloat("myFloatValue");
        await Console.Error.WriteLineAsync("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }}

日历

实现ICalendar接口的Quartz.NET Calendar对象可以在触发器存储在调度程序中时与触发器相关联。日历可用于从触发器的发射计划中排除时间块。例如,您可以创建一个触发器,在每个工作日上午9:30触发作业,但随后添加一个排除所有业务假期的日历。


必须通过AddCalendar(..)方法实例化日历并向调度程序注册日历。如果您使用HolidayCalendar,则在实例化之后,您应该使用其AddExcludedDate(DateTime date)方法,以便使用您希望从计划中排除的日期填充它。相同的日历实例可以与多个触发器一起使用

日历示例 将跳过在日历排除的期间内发生的任何发射。

 

    HolidayCalendar cal = new HolidayCalendar();
    cal.AddExcludedDate(someDate);
    
    await sched.AddCalendar("myHolidays", cal, false);
    
    ITrigger t = TriggerBuilder.Create()
        .WithIdentity("myTrigger")
        .ForJob("myJob")
        .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(9, 30)) // execute job daily at 9:30
        .ModifiedByCalendar("myHolidays") // but not on holidays
        .Build();
    // .. schedule job with trigger
    ITrigger t2 = TriggerBuilder.Create()
        .WithIdentity("myTrigger2")
        .ForJob("myJob2")
        .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(11, 30)) // execute job daily at 11:30
        .ModifiedByCalendar("myHolidays") // but not on holidays
        .Build();

 

SimpleTrigger

如果您需要在特定时刻执行一次作业,或者在特定时刻执行作业,然后按特定时间间隔重复,则SimpleTrigger应满足您的日程安排需求。例如:你想让触发器在2005年1月13日上午11:23:54发射,那么每10秒再发射5次

 

SimpleTrigger的属性包括:开始时间,结束时间,重复计数和重复间隔

重复计数可以是零,正整数或常量值SimpleTrigger.RepeatIndefinitely。

重复间隔属性必须是TimeSpan.Zero或正TimeSpan值。

请注意,重复间隔为零将导致触发器的“重复计数”触发同时发生(或者与调度程序可以管理的同时接近)

 

SimpleTrigger实例是使用TriggerBuilder(用于触发器的主要属性)和WithSimpleSchedule扩展方法(用于SimpleTrigger特定属性)构建的。

在特定时刻构建触发器,不重复:

 

// trigger builder creates simple trigger by default, actually an ITrigger is returnedISimpleTrigger trigger = (ISimpleTrigger) TriggerBuilder.Create()
    .WithIdentity("trigger1", "group1")
    .StartAt(myStartTime) // some Date 
    .ForJob("job1", "group1") // identify job with name, group strings
    .Build();

 

在特定时刻构建触发器,然后每十秒重复十次:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .StartAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
    .WithSimpleSchedule(x => x
        .WithIntervalInSeconds(10)
        .WithRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
    .ForJob(myJob) // identify job with handle to its JobDetail itself                   
    .Build();

构建一个触发器,将在未来五分钟内触发:

 

trigger = (ISimpleTrigger) TriggerBuilder.Create()
    .WithIdentity("trigger5", "group1")
    .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute)) // use DateBuilder to create a date in the future
    .ForJob(myJobKey) // identify job with its JobKey
    .Build();

 

构建一个现在将触发的触发器,然后每五分钟重复一次,直到22:00:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger7", "group1")
    .WithSimpleSchedule(x => x
        .WithIntervalInMinutes(5)
        .RepeatForever())
    .EndAt(DateBuilder.DateOf(22, 0, 0))
    .Build();

构建一个触发器,在下一个小时的顶部触发,然后每2小时重复一次,永远:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group
    .StartAt(DateBuilder.EvenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))
    .WithSimpleSchedule(x => x
        .WithIntervalInHours(2)
        .RepeatForever())
    // note that in this example, 'forJob(..)' is not called 
    //  - which is valid if the trigger is passed to the scheduler along with the job  
    .Build();await scheduler.scheduleJob(trigger, job);

CronTrigger

CronTriggers通常比SimpleTrigger更有用,如果您需要基于类似日历的概念而不是精确指定的SimpleTrigger间隔重复发生的作业计划。

使用CronTrigger,您可以指定触发时间表,例如“每周五中午”,或“每个工作日和上午9:30”,甚至“每周一,周三上午9:00到上午10:00之间每隔5分钟”和星期五”。

即便如此,像SimpleTrigger一样,CronTrigger有一个startTime,用于指定计划何时生效,以及一个(可选的)endTime,用于指定何时停止计划。

 

Cron表达式

Cron-Expressions用于配置CronTrigger的实例。Cron-Expressions是实际上由七个子表达式组成的字符串,用于描述计划的各个细节。这些子表达式用空格分隔,表示:

  1. 分钟
  2. 小时
  3. 日的日
  4. 某一天的周
  5. 年份(可选字段)

 建立CronTriggers

 CronTrigger实例是使用TriggerBuilder(用于触发器的主要属性)和WithCronSchedule扩展方法(用于特定于CronTrigger的属性)构建的。

 您还可以使用CronScheduleBuilder的静态方法来创建计划。

建立一个触发器,每天上午8点到下午5点之间每隔一分钟触发一次:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithCronSchedule("0 0/2 8-17 * * ?")
    .ForJob("myJob", "group1")
    .Build();

构建一个触发器,每天上午10:42开火:

// we use CronScheduleBuilder's static helper methods heretrigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(10, 42))
    .ForJob(myJobKey)
    .Build();

要么 -

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithCronSchedule("0 42 10 * * ?")
    .ForJob("myJob", "group1")
    .Build();

构建一个触发器,该触发器将在星期三上午10点42分在TimeZone中触发,而不是系统的默认值:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithSchedule(CronScheduleBuilder
        .WeeklyOnDayAndHourAndMinute(DayOfWeek.Wednesday, 10, 42)
        .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
    .ForJob(myJobKey)
    .Build();

要么 -

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithCronSchedule("0 42 10 ? * WED", x => x
        .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
    .ForJob(myJobKey)
    .Build();

示例Cron表达式

以下是表达式及其含义的更多示例 - 您可以在CronTrigger的API文档中找到更多

CronTrigger示例1 - 用于创建触发器的表达式,该触发器每5分钟触发一次

"0 0/5 * * * ?"

CronTrigger示例2 - 用于创建触发器的表达式,该触发器在每分钟10秒后(即上午10:00:10,上午10:05:10等)每隔5分钟触发一次。

"10 0/5 * * * ?"

CronTrigger示例3 - 用于创建触发器的表达式,该触发器在每周三和周五的10:30,11:30,12:30和13:30触发。

"0 30 10-13 ? * WED,FRI"

CronTrigger示例4 - 用于创建触发器的表达式,该触发器在每个月的5日和20日上午8点到上午10点之间每半小时触发一次。请注意,触发器不会在上午10:00,即8:00,8:30,9:00和9:30触发

"0 0/30 8-9 5,20 * ?"

请注意,某些计划要求过于复杂,无法通过单个触发器表达 - 例如“上午9:00至上午10:00之间每隔5分钟,下午1:00至晚上10:00之间每20分钟”。此方案中的解决方案是简单地创建两个触发器,并注册它们以运行相同的作业

 

原文地址:https://www.cnblogs.com/AndyChen2015/p/9571533.html