Quartz Cron 触发器
当您需要基于类似日历的概念(而非简单触发器的精确指定间隔)来制定作业触发计划时,CronTrigger 往往比 SimpleTrigger 更为有用。
使用 CronTrigger,您可以指定诸如“每周五中午”、“每个工作日上午 9:30”或甚至是“每周一、三、五的上午 9:00 到 10:00 之间每 5 分钟”的触发计划。
即便如此,CronTrigger 和 SimpleTrigger 一样,都有一个 startTime 属性来指定何时开始执行此计划,以及一个可选的 endTime 属性来指定何时停止此计划。
Cron 表达式
Cron 表达式是一个由 6 或 7 个字段组成,用空格分隔的字符串。各字段可以包含允许的任何值,以及该字段允许的特殊字符组合。字段如下:
字段名 | 必填 | 允许的值 | 允许的特殊字符 |
---|---|---|---|
秒钟 | 是 | 0-59 | , - * / |
分钟 | 是 | 0-59 | , - * / |
小时 | 是 | 0-23 | , - * / |
月份中的日期 | 是 | 1-31 | , - * ? / L W |
月份 | 是 | 1-12 或 JAN-DEC | , - * / |
星期中的日期 | 是 | 1-7 或 SUN-SAT | , - * ? / L # |
年份 | 否 | 空, 1970-2099 | , - * / |
一个完整的 cron 表达式的例子是字符串 0 0 12 ? * WED
,意味着 “每周三中午 12 点”。
各个子表达式可以包含范围和/或列表。例如,之前示例中的星期几字段(写着 "WED")可以被替换成"MON-FRI"、"MON, WED, FRI",甚至是 "MON-WED,SAT"。
通配符(*
字符)可以用来表示该字段的 “每个” 可能值。因此,前一个例子中 “月份” 字段的 *
字符简单表示“每个月”。星号 *
在星期几字段显然意味着 “一周中的每一天”。
所有字段都有一组可以指定的有效值。这些值应该是相当直观的,比如秒和分钟的 0 到 59,小时的 0 到 23。月份中的日期可以是 1 到 31 之间的任意值,但需要注意每个月的天数是不一样的!月份可以用 1 到 12 之间的值或 JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 和 DEC 这些字符串来指定。星期几可以用 1 到 7(1=周日)或 SUN, MON, TUE, WED, THU, FRI, SAT 这些字符串来指定。
斜杠 /
字符可用于指定值的增量。例如,如果你在分钟字段放入 '0/15',它的意思是“每 15 分钟,从第 0 分钟开始”。如果你在分钟字段使用 '3/20',它意味着 “每小时的 20 分钟,从第 3 分钟开始”——换句话说,这等同于在分钟字段指定 '3,23,43'。
问号 ?
字符允许用于月份中的日期和星期几字段。它用来指定 “无特定值”。当你需要在一个字段中指定某些值,而不指定另一个字段时,这非常有用。请参阅下面的示例(和 CronTrigger API 文档)以获得澄清。
字符 L
允许用于月份中的日期和星期几字段。在这个两个字段中,这个字符代表 “最后”,但含义有所不同。例如,月份中的日期字段中的 L
意味着 "一个月的最后一天"——1 月的 31 日,非闰年的 2 月的 28 日。如果单独在星期几字段中使用 L
,它简单意味着 "7" 或 "SAT"。但如果在星期几字段的其他值之后使用 L
,它意味着 “一个月的最后一个 xxx 天” ——例如,"6L" 或 "FRIL" 都表示 “一个月的最后一个周五”。使用L
选项时,重要的是不要指定列表或值的范围,否则你会得到混淆的结果。
W
字符用于指定离给定日期最近的工作日(周一至周五)。例如,如果在月份中的日期字段指定 "15W",其含义是:“一个月中最接近 15 号的工作日”。
#
字符用于指定 “每月的第 n 个” 星期几。例如,星期几字段中的值 "6#3" 或 "FRI#3" 意味着 “一个月的第三个星期五”。
Cron 表达式的示例
这里有一些表达式及其含义的更多示例,您还可以在 CronTrigger 的 API 文档中找到更多。
CronTrigger 示例 1 - 创建一个每 5 分钟触发一次的触发器
"0 0/5 * * * ?"
CronTrigger 示例 2 - 创建一个每 5 分钟,在每分钟的第 10 秒触发的触发器(例如上午 10:00:10,10:05:10 等)。
"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 am 触发,只会在 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 分钟”。在这种情况下,解决方案是简单地创建两个触发器,并都注册运行同一个作业。
构建 CronTrigger
CronTrigger 实例使用 TriggerBuilder
(用于触发器的主要属性)和 WithCronSchedule
扩展方法(用于 CronTrigger 特有的属性)构建。
您也可以使用 CronScheduleBuilder
的静态方法来创建调度。
构建一个每分钟触发一次的触发器,时间在每天的 8am 到 5pm 之间:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 0/2 8-17 * * ?")
.ForJob("myJob", "group1")
.Build();
构建一个每天上午 10:42 触发的触发器:
// 这里我们使用 CronScheduleBuilder 的静态辅助方法
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(10, 42))
.ForJob(myJobKey)
.Build();
或
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 42 10 * * ?")
.ForJob("myJob", "group1")
.Build();
构建一个在周三上午 10:42,在非系统默认时区触发的触发器:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithSchedule(CronScheduleBuilder
.WeeklyOnDayAndHourAndMinute(DayOfWeek.Wednesday, 10, 42)
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time"))))
.ForJob(myJobKey)
.Build();
或
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 42 10 ? * WED", x => x
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time"))))
.ForJob(myJobKey)
.Build();
CronTrigger 误触发指令
以下指令可以用来告知 Quartz 当 CronTrigger 发生误触发时应如何处理。(误触发的情况在本教程的“关于触发器的更多内容”部分已经介绍过)。这些指令定义为常量(API 文档有它们行为的描述)。指令包括:
MisfireInstruction.IgnoreMisfirePolicy
MisfireInstruction.CronTrigger.DoNothing
MisfireInstruction.CronTrigger.FireOnceNow
所有触发器都有 MisfireInstrution.SmartPolicy
指令可用,并且这也是所有触发器类型的默认指令。对于 CronTrigger,“智能策略” 指令被解释为 MisfireInstruction.CronTrigger.FireOnceNow
。 CronTrigger.UpdateAfterMisfire()
方法的 API 文档详细解释了这一行为的具体细节。
构建 CronTrigger 时,您可以通过 WithCronSchedule
扩展方法的一部分指定误触发指令:
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 0/2 8-17 * * ?", x => x
.WithMisfireHandlingInstructionFireAndProceed())
.ForJob("myJob", "group1")
.Build();