Quartz.NET 实现了3个具体的触发器类,SimpleTrigger 简单地在某一时间重复执行多少次,NthIncludedDayTrigger 在每一年、月、周的第几天(Nth)执行作业,CronTrigger 使用 Unix 平台下的'cron-like’表达式来实现非常灵活的触发时间。

Quartz.NET--Trigger 触发器_触发器


SimpleTrigger


 它有一个起始时间和结束时间,起始时间触发器触发,过了结束时间触发器停止触发。时间间隔 Interval,触发次数 RepeatCount。


Quartz.NET--Trigger 触发器_字段_02

如果一个作业,在指定的时间点执行一次,或者间隔时间内重复执行,SimpleTrigger就可以满足这样的要求。简而言之,2005年1月13日11点23分54秒触发,每10分钟触发5次。
  显而易见,SimpleTrigger包括开始时间,结束时间,重复次数,间隔。重复次数可以为零,正整数,SimpleTrigger.RepeatIndefinitely常量。间隔时间可以为TimeSpan.Zero,或正的TimeSpan.Value。
注:间隔时间为TimeSpan.Zero时,会导致同时触发(并发)。



SimpleTrigger有几个不同的构造器:


public  SimpleTrigger( string  name,  string  group, DateTime startTimeUtc, NullableDateTime endTime endTimeUtc,  int  repeatCount, TimeSpan repeatInterval)



SimpleTrigger Example 1 - 从现在开始10分钟后触发一次


SimpleTrigger trigger = new SimpleTrigger("myTrigger", null, DateTime.UtcNow.AddSeconds(10), null, 0, TimeSpan.Zero);


SimpleTrigger Example 2 - 每60分钟响应一次,立即执行

SimpleTrigger trigger2 = new SimpleTrigger("myTrigger", null, DateTime.UtcNow, null, SimpleTrigger.RepeatIndefinitely, TimeSpan.FromSeconds(60));


SimpleTrigger Example 3 - 每10分钟响应一次,40分钟后开始

SimpleTrigger trigger = new SimpleTrigger("myTrigger", "myGroup", DateTime.UtcNow, DateTime.UtcNow.AddSeconds(40), SimpleTrigger.RepeatIndefinitely, TimeSpan.FromSeconds(10));


SimpleTrigger Example 4 - 2002年3月17日上午10点30分响应,重复5次(共6次触发),每次之间有30分钟间隔

DateTime startTime = new DateTime(2002, 3, 17, 10, 30, 0).ToUniversalTime(); SimpleTrigger trigger = new SimpleTrigger("myTrigger", null, startTime, null, 5, TimeSpan.FromSeconds(30));

花点时间再看看别的SimpleTrigger构造器,您会发现那个对您最方便。

SimpleTrigger过时触发



MisfirePolicy.SimpleTrigger.FireNow MisfirePolicy.SimpleTrigger.RescheduleNowWithExistin gRepeatCount MisfirePolicy.SimpleTrigger.RescheduleNowWithRemaini ngRepeatCount MisfirePolicy.SimpleTrigger.RescheduleNextWithRemain ingCount MisfirePolicy.SimpleTrigger.RescheduleNextWithExisti ngCount


 

   如果用“smart policy”,SimpleTrigger会根据SimpleTrigger实例的状态和配置动态选择过时触发策略。SimpleTrigger.UpdateAfterMisfire()方法介绍中,详细解释了动态表现。


CronTrigger



  CronTrigger 使用 UNIX 下的“Cron-like” 表达式,实际上用起来感觉它很像正则表达式,可以匹配任意时间,这是体现它灵活性的地方。它的规则如下:

Cron 表达式包括以下 7 个字段(1 个可选)

秒  分 小时 月内日期 月 周内日期 年(可选)

表达式的每个数值域都是一个有最大值和最小值的集合,如:秒域和分钟域的集合是0-59,日期域是1-31,月份域是1-12。注意:秒、分、小时字段是从小到大排序的,这是西方人的习惯,所以在使用的时候要小心,不要颠倒过来。

允许值及对应表见表1。

表1. Cron 表达式允许值及对应表

字段 允许值 允许的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 月内日期 1-31 , - * ? / L W C 月 1-12 或者 JAN-DEC , - * / 周内日期 1-7 或者 SUN-SAT , - * ? / L C # 年(可选) 留空, 1970-2099 , - * /

特殊字符意义对应表见表2。


表2. Cron 表达式特殊字符意义对应表

特殊字符

意义

*

匹配所有的值。如:*在分钟的字段域里表示 每分钟

?

只在日期域和星期域中使用。它被用来指定“非明确的值”

-

指定一个范围。如:“10-12”在小时域意味着“10点、11点、12点”

,

指定几个可选值。如:“MON,WED,FRI”在星期域里表示“星期一、星期三、星期五”

/

指定增量。如:“0/15”在秒域意思是没分钟的0,15,30和45秒。“5/15”在分钟域表示没小时的5,20,35和50。符号“*”在“/”前面(如:*/10)等价于0在“/”前面(如:0/10)

L

表示day-of-month和day-of-week域,但在两个字段中的意思不同,例如day-of-month域中表示一个月的最后一天。如果在day-of-week域表示‘7’或者‘SAT’,如果在day-of-week域中前面加上数字,它表示一个月的最后几天,例如‘6L’就表示一个月的最后一个星期五

W

只允许日期域出现。这个字符用于指定日期的最近工作日。例如:如果你在日期域中写 “15W”,表示:这个月15号最近的工作日。所以,如果15号是周六,则任务会在14号触发。如果15好是周日,则任务会在周一也就是16号触发。如果是在日期域填写“1W”即使1号是周六,那么任务也只会在下周一,也就是3号触发,“W”字符指定的最近工作日是不能够跨月份的。字符“W”只能配合一个单独的数值使用,不能够是一个数字段,如:1-15W是错误的

LW

L和W可以在日期域中联合使用,LW表示这个月最后一周的工作日

#

只允许在星期域中出现。这个字符用于指定本月的某某天。例如:“6#3”表示本月第三周的星期五(6表示星期五,3表示第三周)。“2#1”表示本月第一周的星期一。“4#5”表示第五周的星期三

C

允许在日期域和星期域出现。这个字符依靠一个指定的“日历”。也就是说这个表达式的值依赖于相关的“日历”的计算结果,如果没有“日历”关联,则等价于所有包含的“日历”。如:日期域是“5C”表示关联“日历”中第一天,或者这个月开始的第一天的后5天。星期域是“1C”表示关联“日历”中第一天,或者星期的第一天的后1天,也就是周日的后一天(周一)

示例:

"0 0 0 1 1 ?”               每年元旦1月1日 0 点触发

"0 15 10 * * ? *"         每天上午10:15触发  
"0 15 10 * * ? 2005"   2005年的每天上午10:15触发

"0 0-5 14 * * ?"          每天下午2点到下午2:05期间的每1分钟触发  
"0 10,44 14 ? 3 WED"  每年三月的星期三的下午2:10和2:44触发  
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发

"0 15 10 ? * 6#3"        每月的第三个星期五上午10:15触发


触发器

像Job一样,触发器相对来说还是比较容易实现的。但是要充分利用Quartz.Net,还需要透彻理解触发器的几个自定义选项。早先我们提到过,有几个不同类型的触发器,可以选择不同类型的触发器来满足不同作业的需要。

Calendars



namespace Quartz 
{
public interface ICalendar
{
string Description { get; set; }
ICalendar CalendarBase { set; get; }
bool IsTimeIncluded(DateTime timeUtc);
DateTime GetNextIncludedTimeUtc(DateTime timeUtc);
}
}

 



HolidayCalendar cal =  new  HolidayCalendar();


cal.AddExcludedDate(someDate);


sched.AddCalendar(

"myHolidays" , cal,  false ); 


// fire every one hour interval


Trigger trigger = TriggerUtils.MakeHourlyTrigger(); 


// start on the next even hour


trigger.StartTimeUtc = TriggerUtils.GetEvenHourDate(DateTime.Now);


trigger.Name = 

"myTrigger1" ;


trigger.CalendarName = 

"myHolidays" ; 


// .. schedule job with trigger

 


// fire every day at 08:00


Trigger trigger2 = TriggerUtils.MakeDailyTrigger(8, 0); 


// begin immediately


trigger.StartTimeUtc = DateTime.UtcNow;


trigger2.Name = 

"myTrigger2" ;


trigger2.CalendarName = 

"myHolidays" ;


// .. schedule job with trigger2




Priority


Note: 当一个Trigger被恢复的时候,也是按照他们原来的优先级来恢复的。


Priority Example

// All three Triggers will be scheduled to fire 5 minutes from now. 
DateTime d = DateTime.UtcNow.AddMinutes(5);
Trigger trig1 = new SimpleTrigger("T1", "MyGroup", d);
Trigger trig2 = new SimpleTrigger("T2", "MyGroup", d);
Trigger trig3 = new SimpleTrigger("T3", "MyGroup", d);
JobDetail jobDetail = new JobDetail("MyJob", "MyGroup", typeof(NoOpJob));
// Trigger1 does not have its priority set, so it defaults to 5
sched.ScheduleJob(jobDetail, trig1);
// Trigger2 has its priority set to 10
trig2.JobName = jobDetail.Name;
trig2.Priority = 10;
sched.ScheduleJob(trig2);
// Trigger2 has its priority set to 1
trig3.JobName = jobDetail.Name;
trig3.Priority = 1;
sched.ScheduleJob(trig3);
// Five minutes from now, when the scheduler invokes these three triggers
// they will be allocated worker threads in decreasing order of their
// priority: Trigger2(10), Trigger1(5), Trigger3(1)

过时触发Misfire Instructions



TriggerUtils - 让Triggers更容易


Trigger Listeners监听器

Trigger能够像Job一样,可以把监听器注册到Trigger中,实现了接口TriggerListener的对象就可以接收到Trigger触发时的通知。


CronTrigger


如果需要像日历一样重复发生的作业调度的话,CronTriggers就比特别指定间隔时间的SimpleTrigger,更有用。

运用CronTrigger, 可以设定的作业调度像,每周五下午,每工作日的上午9点,每周1、2、5的上午9点到10点。

和SimpleTrigger一样, 有效的CronTrigger有一个开始时间,一个当调度不再继续的结束时间,结束时间是可选的。

Cron Expressions表达式

Cron-Expressions是用来配置CronTrigger实例的。 Cron-Expressions是由7个子字符串表达式组成,用来详细描述schedule的。这些表达式以空格分开:

  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (optional field)

如一个完整的"0 0 12 ? * WED"表示"每周三晚上12"。



所有的字段都可以指定具体的值。这些值需要比较明确,如0-59表示分钟、秒,0-23表达小时。0-31表示日期,但您得指定的月份有多少天哦!月份可以指定为0-11, 或者缩写JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV,DEC。 工作日1-7 (1是星期日),或用字符串SUN, MON, TUE, WED, THU, FRI and SAT表示。

'/'字符可以表示成递增的值。如, 如果分钟的字段上'0/15', 表示每15分钟,从0分钟开始。如果分钟的字段上是'3/20',则表示在一个小时中每20分钟、起点是3。换句话说,在分钟的字段上'3,23,43',与之等效。

'?'字符,可以用在一个月的某天,一个星期的某天。是不特指的意思。当在这两个字段中,不特指的时候很方便使用。下面的CronTriger API中将有详细阐述。

'L'字符串,可以用在一个月的某天,一个星期的某天。是"last"的缩写。但在两个字段中表达的意思是不一样的。例如"L"在月份的某天时,一个月的最后一天,30,31,28。如果在工作日的某天时,只是表示"7"或者"SAT"。但是如果在其他值后面,用在工作日字段中的话,即表示“那个月最后一个周几”。

'W'字符串,可以用来指定一个月的第几天那个工作日。"15W",表示某月的第15天那个工作日。

'#'字符串,可以用来指定一个月的第几个工作日。"6#3" 或者"FRI#3",表示一个月的第3个星期5。.


   注:有些调度用一个触发器的话,表达式会很复杂。如上午9-10,以及下午1点到10点没20分钟一次。这种场景适合建立两个简单的触发器,两个触发器都运行同一个作业。

CronTrigger过时触发

下面介绍CronTrigger的过时触发。 这些过时触发定义在常量MisfireInstruction.CronTrigger中:

  • DoNothing
  • FireOnceNow

所有触发器都有MisfireInstrution.SmartPolicy过时触发, 且作为缺省的过时触发。这种策略'smart policy'可在CronTrigger这里,以理解为 MisfireInstruction.CronTrigger.FireOnceNow。CronTrigger.UpdateAfterMisfire()方法,在后续有更纤细的阐述。


TriggerListeners and JobListeners


Listeners是用来监听在调度作业有执行动作的对象。TriggerListeners用来接收和triggers相关的事件,JobListeners用来接收和作业有关系的事件.

Trigger相关的事件: 触发器触发,触发器过期触发,触发器结束。


ITriggerListener接口

public interface ITriggerListener 
{
string Name { get; }
void TriggerFired(Trigger trigger, JobExecutionContext context);
bool VetoJobExecution(Trigger trigger, JobExecutionContext context);
void TriggerMisfired(Trigger trigger);
void TriggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode); }

作业相关的时间包括: 作业执行提示,作业结束提示。


IJobListener接口

public interface IJobListener 
{
string Name { get; }
void JobToBeExecuted(JobExecutionContext context);
void JobExecutionVetoed(JobExecutionContext context);
void JobWasExecuted(JobExecutionContext context, JobExecutionException jobException);
}

用自己的Listeners

创建listener很简单, 创建一个继承ITriggerListener或者IJobListener接口的一个对象。Listeners在调度运行时注册,通过Name属性指定的名称。可以注册成"global" 或者 "non-global"。全局listeners接收所有触发器/作业,non-global监听器接收通过GetTriggerListenerNames() 或者 GetJobListenerNames()获得的事件。

listeners在调度运行的时候注册的,不是与作业和触发器一起存放在JobStore中。jobs与triggers,只是存放那些与之相关的坚听器的名称。因此,每次应用运行时,listeners是需要在调度中重新注册的。


向 Scheduler 添加 JobListener

scheduler.AddGlobalJobListener(myJobListener);


or

scheduler.AddJobListener(myJobListener);


常在Quartz.Net中使用,但是如果需要事件提示就随手可见,不需要作业明示的提醒应用。