Quartz.NET--Trigger 触发器

来源:互联网 发布:js代码大全 编辑:程序博客网 时间:2024/06/10 03:09

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


SimpleTrigger

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


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

  假如还不太熟悉DateTime类的话,您会发现startTimeUtc或endTimeUtc在计算触发时间的时候,非常有用。TriggerUtils在这方面也是很有帮助。

  EndTimeUtc属性(除非特指)覆盖重复次数属性。如果您希望创建一个指定时间开始,每10分钟触发的触发器,就很有用处,没必要在制定开始与结束时间内计算重复次数。

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过时触发

  SimpleTrigger有几个过时触发。更多过时触发方面的阐述已经在前面章节中阐述过。 SimpleTrigger的过时触发定义成约束,MisfirePolicy.SimpleTrigger:

MisfirePolicy.SimpleTrigger.FireNow MisfirePolicy.SimpleTrigger.RescheduleNowWithExistingRepeatCount MisfirePolicy.SimpleTrigger.RescheduleNowWithRemainingRepeatCount MisfirePolicy.SimpleTrigger.RescheduleNextWithRemainingCount MisfirePolicy.SimpleTrigger.RescheduleNextWithExistingCount
   如果用“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

  Calendar对象是和保存在scheduler中的trigger相关联的。当在调度中,需要排除一个时间段,Calendar就非常好用。举个例子来说,创建这样一个触发器,每个工作日的9:30触发一个作业,并且要排除所有法定假日。

  Calendar是能被序列化的任何对象,继承ICalendar接口,如下:

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

   注意,这里的参数都是long type的日期值。不出您的意料,他们都精确到毫秒级。这就意味着,calendar能“卡位”到毫秒级的时间段。通常情况下,我们队“卡位”到天,更感兴趣。为了方便,Quartz提供 HolidayCalendar类, 意图这是如此。

  Calendar必须通过AddCalendar(..)方法,来初始化及注册。如果使用HolidayCalendar类的话,在实例化以后,得必须用AddExcludedDate(DateTime date)方法来添加,以便在调度中排除。多个trigger可以使用同一个calendar实例:

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

  SimpleTrigger构造器的参数值,将会在下个章节中阐述。那么就目前来说,只要确信,以上代码是创建这样两个触发器:一个是每60分钟执行一次,循环往复知道永远;另一个是每间隔5天执行,重复5次。但是,任何calendar排除在外的触发,都将会跳过。

Priority

  有时候,当有很多trigger(或者Quartz线程池中只有位数不多的工作线程),Quart.Net没有足够的资源,在同一个时间内,Quart.Net就不会响应所有的触发。在这种情况下,就需要控制那些触发器,优先获得Quartz.NET 工作线程。基于这个目的,就需要在trigger上设置优先级Priority。如果N个Trigger同时触发,但是这时只有Z个工作线程可用,那么只能是优先级前Z的trigger获得。如果不设置Trigger上的Priority,缺省的优先级是5。优先级的值,可以是任何整数值,正数,或者是负数。

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

  另一个重要的属性是,过时触发"misfire instruction"。 当一个作业被"Shutdown"的时候,持久触发器就会“丢失”触发事件,出现“过时触发”。不同类型的触发器,有不同的过时触发。缺省情况下,使用“smart policy”过时触发——根据不同触发器类型和配置有不同的行为表现。当scheduler启动的时候,它会查询所有过时触发的持久触发器,根据他们各自的过时触发配置更新它们。

  在使用Quartz之前,就必须了解自己定义的触发器类型,以及他们的过时触发。一个特定的触发器的过时触发,可以通过MisfireInstruction属性类配置设定。

TriggerUtils - 让Triggers更容易

  有了TriggerUtils类,不需要再煞费苦心的用DateTime对象了,它为我们创建触发器、日期提供了方便。 这个类可以非常轻松地设置触发器何时触发执行,按分钟、小时、天、星期、月等等——设置触发器起始时间非常有用。

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"。

  单个子表达式可以包含范围、列表。例如前面那个表达式中的"WED",可以用"MON-FRI", "MON, WED, FRI", 或者 甚至用"MON-WED,SAT"代替。

  通配符('*'),在相应字段中可以用来表达“每”的意思。在刚才那个例子中Month字段位置就表达“每月”。在工作日地段位置就表达“每个工作日”。

 所有的字段都可以指定具体的值。这些值需要比较明确,如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);

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