Quartz的misfire处理机制分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Quartz的misfire处理机制分析相关的知识,希望对你有一定的参考价值。

Quartz是一个特性丰富的开源的任务调度开发库,它可以很方便的集成到你的应用程序中。在Quartz中,当一个持久的触发器因为调度器被关闭或者线程池中没有可用的线程而错过了激活时间时,就会发生激活失败(misfire)。那么,我们需要明确2个问题:如何判定激活失败;如何处理激活失败。

一、激活失败判定

quartz.properties配置文件中有一个属性是misfireThreshold(单位为毫秒),用来指定调度引擎设置触发器超时的"临界值"。也就是说Quartz对于任务的超时是有容忍度的,超过了这个容忍度才会判定misfire。比如说,某触发器设置为,10:15首次激活,然后每隔3秒激活一次,无限次重复。然而该任务每次运行需要10秒钟的时间。可见,每次任务的执行都会超时,那么究竟是否会引起misfire,就取决于misfireThreshold的值了。以第二次任务来说,它的运行时间已经比预定晚了7秒,那么如果misfireThreshold>7000,说明该偏差可容忍,则不算misfire,该任务立刻执行;如果misfireThreshold<=7000,则判定为misfire,根据相关配置策略进行处理。

注意,任务的延迟是有累计的。在前面的例子中,假设misfireThreshold设置为60000,即60秒。那么每次任务的延迟量即是否misfire计算如下:

任务编号

预定运行时刻

实际运行时刻

延迟量(秒)

备注

1

10:15

10:15

0

 

2

10:18

10:25

7

 

3

10:21

10:35

14

 

4

10:24

10:45

21

 

5

10:27

10:55

28

 

6

10:30

11:05

35

 

7

10:33

11:15

42

 

8

10:36

11:25

49

 

9

10:39

11:35

56

 

10

10:42

11:45

63

misfire

从表中可以看到,每一次任务执行都会带来7秒的延迟量,该延迟量不断被与misfireThreshold比较,直到达到该值后,在11:45分发生misfire。那么在11:4510次任务会不会准时执行呢?答案是不一定,取决于配置。

二、激活失败处理

激活失败指令(Misfire Instructions)是触发器的一个重要属性,它指定了misfire发生时调度器应当如何处理。所有类型的触发器都有一个默认的指令,叫做Trigger.MISFIRE_INSTRUCTION_SMART_POLICY,但是这个这个“聪明策略”对于不同类型的触发器其具体行为是不同的。对于SimpleTrigger,这个“聪明策略”将根据触发器实例的状态和配置来决定其行为。具体如下[]:

如果Repeat Count=0

instruction selected = MISFIRE_INSTRUCTION_FIRE_NOW;

如果Repeat Count=REPEAT_INDEFINITELY:

instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT;

如果Repeat Count>0

instruction selected = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT;

下面解释SimpleTrigger常见策略:

MISFIRE_INSTRUCTION_FIRE_NOW

立刻执行。对于不会重复执行的任务,这是默认的处理策略。

MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

NEXT指以现在为基准,以repeat interval为周期,延时到下一个激活点执行。WITH_REMAINING_COUNT指超时期内错过的执行机会作废。因此该策略的含义是,在下一个激活点执行,且超时期内错过的执行机会作废。

MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_COUNT

立即执行,且超时期内错过的执行机会作废。

MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

WITH_EXISTING_COUNT指,根据已设置的repeat count进行执行。也就是说错过的执行机会不作废,保证实际执行次数满足设置。因此本策略的含义是,在下一个激活点执行,并重复到指定的次数。

MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT

立即执行,并重复到指定的次数。

MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

忽略所有的超时状态,按照触发器的策略执行。

对于CronTrigger,该“聪明策略”默认选择MISFIRE_INSTRUCTION_FIRE_ONCE_NOW以指导其行为。

下面解释CronTrigger常见策略:

MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

立刻执行一次,然后就按照正常的计划执行。

MISFIRE_INSTRUCTION_DO_NOTHING

目前不执行,然后就按照正常的计划执行。这意味着如果下次执行时间超过了end time,实际上就没有执行机会了。

 

三、示例程序1

=====代码=====

JobDetail job1 = newJob(StatefulDumbJob.class).withIdentity("statefulJob1", "group1")
        .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build();

    SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime)
        .withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();

    Date ft1 = sched.scheduleJob(job1, trigger1);
    log.info(job1.getKey() + " will run at: " + ft1 + " and repeat: " + trigger1.getRepeatCount() + " times, every "
             + trigger1.getRepeatInterval() / 1000 + " seconds");

=====代码=====

misfireThreshold设置为60000

分析:

各任务的延迟量如下表所示:

任务编号

预定运行时刻

实际运行时刻

延迟量(秒)

备注

1

16:15:45

16:15:45

0

 

2

16:15:48

16:15:55

7

 

3

16:15:51

16:16:05

14

 

4

16:15:54

16:16:15

21

 

5

16:15:57

16:16:25

28

 

6

16:16:00

16:16:35

35

 

7

16:16:03

16:16:45

42

 

8

16:16:06

16:16:55

49

 

9

16:16:09

16:17:05

56

 

16:16:12

16:17:15

63

misfire

10

16:17:18

16:17:18

0

 

11

16:17:21

16:17:28

7

 

 

16:17:15发生misfire。因为Repeat Count=REPEAT_INDEFINITELY,所以smart policy选择的策略是MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT。因此,延时3秒后,在16:17:1810次任务被执行,延迟量清零。当然,程序会一直这样循环下去。

程序输出:

---group1.statefulJob1 executing.[Fri Feb 19 16:15:45 CST 2016]

  -group1.statefulJob1 complete (1).

---group1.statefulJob1 executing.[Fri Feb 19 16:15:55 CST 2016]

  -group1.statefulJob1 complete (2).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:05 CST 2016]

  -group1.statefulJob1 complete (3).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:15 CST 2016]

  -group1.statefulJob1 complete (4).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:25 CST 2016]

  -group1.statefulJob1 complete (5).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:35 CST 2016]

  -group1.statefulJob1 complete (6).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:45 CST 2016]

  -group1.statefulJob1 complete (7).

---group1.statefulJob1 executing.[Fri Feb 19 16:16:55 CST 2016]

  -group1.statefulJob1 complete (8).

---group1.statefulJob1 executing.[Fri Feb 19 16:17:05 CST 2016]

  -group1.statefulJob1 complete (9).

---group1.statefulJob1 executing.[Fri Feb 19 16:17:18 CST 2016]

  -group1.statefulJob1 complete (10).

四、示例程序2

=====代码=====

JobDetail job2 = newJob(StatefulDumbJob.class).withIdentity("statefulJob2", "group1")
        .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build();

    SimpleTrigger trigger2 = newTrigger()
        .withIdentity("trigger2", "group1")
        .startAt(startTime)
        .withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever()
                          .withMisfireHandlingInstructionNowWithExistingCount()) // set misfire instructions
        .build();

    Date ft2 = sched.scheduleJob(job2, trigger2);
    log.info(job2.getKey() + " will run at: " + ft2 + " and repeat: " + trigger2.getRepeatCount() + " times, every "
             + trigger2.getRepeatInterval() / 1000 + " seconds");

=====代码=====

misfireThreshold设置为60000

分析:

示例2与示例1的区别在于,触发器指定了激活失败指令为MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT。

各任务的延迟量如下表所示:

任务编号

预定运行时刻

实际运行时刻

延迟量(秒)

备注

1

17:05:15

17:05:15

0

 

2

17:05:18

17:05:25

7

 

3

17:05:21

17:05:35

14

 

4

17:05:24

17:05:45

21

 

5

17:05:27

17:05:55

28

 

6

17:05:30

17:06:05

35

 

7

17:05:33

17:06:15

42

 

8

17:05:36

17:06:25

49

 

9

17:05:39

17:06:35

56

 

17:05:42

17:06:45

63

misfire

10

17:06:45

17:06:45

0

 

11

17:05:48

17:06:55

7

 

17:06:45发生misfire。根据其激活失败指令,任务10在该时刻被立即执行,延迟量清零。然后,程序继续如此循环下去。

程序输出:

---group1.statefulJob2 executing.[Fri Feb 19 17:05:15 CST 2016]
-group1.statefulJob2 complete (1).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:25 CST 2016]
-group1.statefulJob2 complete (2).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:35 CST 2016]
-group1.statefulJob2 complete (3).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:45 CST 2016]
-group1.statefulJob2 complete (4).
---group1.statefulJob2 executing.[Fri Feb 19 17:05:55 CST 2016]
-group1.statefulJob2 complete (5).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:05 CST 2016]
-group1.statefulJob2 complete (6).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:15 CST 2016]
-group1.statefulJob2 complete (7).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:25 CST 2016]
-group1.statefulJob2 complete (8).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:35 CST 2016]
-group1.statefulJob2 complete (9).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:45 CST 2016]
-group1.statefulJob2 complete (10).
---group1.statefulJob2 executing.[Fri Feb 19 17:06:55 CST 2016]
-group1.statefulJob2 complete (11).

以上是关于Quartz的misfire处理机制分析的主要内容,如果未能解决你的问题,请参考以下文章

quartz-misfire 错失补偿执行

源码分析ElasticJob任务错过机制(misfire)与幂等性

quartz2.3.0制定错过执行任务的misfire策略

quartz中misfirm处理机制

Quartz调度系统入门和调度高可用实现方案

quartz配置参数org.quartz.jobStore.misfireThreshold含义解释