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 , - * /
提示
为了方便地使用 UI 生成 cron 间隔,您可以使用以下服务之一:

一个完整的 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.FireOnceNowCronTrigger.UpdateAfterMisfire() 方法的 API 文档详细解释了这一行为的具体细节。

构建 CronTrigger 时,您可以通过 WithCronSchedule 扩展方法的一部分指定误触发指令:

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithCronSchedule("0 0/2 8-17 * * ?", x => x
        .WithMisfireHandlingInstructionFireAndProceed())
    .ForJob("myJob", "group1")
    .Build();
在本文档中