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是实际上由七个子表达式组成的字符串,用于描述计划的各个细节。这些子表达式用空格分隔,表示:
- 秒
- 分钟
- 小时
- 日的日
- 月
- 某一天的周
- 年份(可选字段)
建立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分钟”。此方案中的解决方案是简单地创建两个触发器,并注册它们以运行相同的作业。