Quartz Misfire策略
一、Misfire的原因
1.当job达到触发时间时,所有线程都被其他job占用,没有可用线程。
2.在job需要触发的时间点,scheduler停止了(可能是意外停止的)。
3.job使用了@DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没有完成。
4.job指定了过去的开始执行时间,例如当前时间是8点00分00秒,指定开始时间为7点00分00秒
二、MisFire的前置条件
misfire产生需要有2个前置条件,一个是job到达触发时间时没有被执行,二是被执行的延迟时间超过了Quartz配置的misfireThreshold阀值。如果延迟执行的时间小于阀值,则Quartz不认为发生了misfire,立即执行job;如果延迟执行的时间大于或者等于阀值,则被判断为misfire,然后会按照指定的策略来执行。
例如:没有配置Quartz的misfireThreshold,此时使用Quartz的默认misfireThreshold配置为60秒(misfireThreshold是可以进行配置的),设置一个job在上午8点执行,由于一些原因job在8点没有执行,分为两种情况:
第一种情况是在8点00分50秒Quartz有资源来执行这个job,此时的延迟执行时间是50秒,小于misfireThreshold为60秒的阀值,则Quartz认为该job没有发生misfire,立即执行job。
第二种情况是在8点10分00秒Quartz有资源来执行这个job,此时延迟执行时间是600秒,大于misfireThreshold为60秒的阀值,则Quartz认为该job发生了misfire,会根据指定的misfire策略来执行。
三、Misfire的处理策略
仅说明CronTrigger的Misfire策略,SimppleTrigger实际不常用。
假设8:00的任务执行了,但是由于某些原因,scheduler没有执行9:00和10:00的任务,在10:15分的时候scheduler发现job有两次没有执行,这两次的延迟执行时间分别是1小时15分和15分,都大于设置的misfireThreshold=1秒,因此发生了两次misfire。各misfire策略如下:
策略 | 说明 |
---|---|
MISFIRE_INSTRUCTION_SMART_POLICY默认 | 等同于MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。 |
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY | Quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按照原计划进行执行。例如:10:15分立即执行9:00和10:00的任务,然后等待下一个任务在11:00执行,后续按照原计划执行。 |
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW | 立即执行第一个发生misfire的任务,忽略其他发生misfire的任务,然后按照原计划继续执行。例如:在10:15立即执行9:00任务,忽略10:00任务,然后等待下一个任务在11:00执行,后续按照原计划执行 |
MISFIRE_INSTRUCTION_DO_NOTHING | 所有发生misfire的任务都被忽略,只是按照原计划继续执行 |
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY | Quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按照原计划进行执行。例如:10:15分立即执行9:00和10:00的任务,然后等待下一个任务在11:00执行,后续按照原计划执行。 |
四、Quartz后台表
qrtz_fired_triggers存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息.
qrtz_triggers 存储已配置的 Trigger的信息
TRIGGER_STATE状态说明:
WAITING: 等待
ACQUIRED:即将触发
EXECUTING:执行中
COMPLETE:不在触发
ERROR:出错
PAUSED: 停止
执行中的job会进入到qrtz_fired_triggers表中,qrtz_triggers表中执行中的任务的状态为blocked。若执行时间过长,超过下次调度时间,则不会触发调度,同时NEXT_FIRE_TIME不会更新。
直到任务完成,若任务完成后的时间减去"本应该触发调度时间"小于等于misfireThreshold,则会被调度起来。
若大于是否被调度依赖于调度策略。
排查SQL语句:
SELECT T.TRIGGER_NAME,
T.TRIGGER_GROUP,
TO_DATE(‘1970/01/01 08:00:00’, ‘YYYY/MM/DD HH24:MI:SS’) +
T.NEXT_FIRE_TIME / (1000 * 60 * 60 * 24) NEXT,
TO_DATE(‘1970/01/01 08:00:00’, ‘YYYY/MM/DD HH24:MI:SS’) +
T.PREV_FIRE_TIME / (1000 * 60 * 60 * 24) PREV,
T.PRIORITY,
T.TRIGGER_STATE,
T.TRIGGER_TYPE,
TO_DATE(‘1970/01/01 08:00:00’, ‘YYYY/MM/DD HH24:MI:SS’) +
T.START_TIME / (1000 * 60 * 60 * 24) STARTTIME,
TO_DATE(‘1970/01/01 08:00:00’, ‘YYYY/MM/DD HH24:MI:SS’) +
T.END_TIME / (1000 * 60 * 60 * 24) ENDTIME
FROM QRTZ_TRIGGERS T;