Quartz Misfire

QuartzMisfire策略
本文介绍了Quartz定时任务中Misfire现象产生的原因及其处理策略。包括Misfire的前置条件、几种常见Misfire策略的含义及应用场景,帮助理解并正确配置Quartz定时任务。

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_POLICYQuartz不会判断发生了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_POLICYQuartz不会判断发生了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;

Quartz中,`misfire_instr`(`MISFIRE_INSTR`)是与触发器的失火(Misfire)处理策略相关的一个属性。失火是指当调度器由于某些原因(如调度器关闭、系统资源不足等)未能在触发器原本设定的时间触发任务时所发生的情况。每个触发器都有一个单独的`misfire_instr`设置,该设置在设置调度的时候进行,并且会保存在`trigger`表的`MISFIRE_INSTR`字段中。只要将这个`misfire`设置为1,就可以在失火之后立即调用一次任务[^1]。 Quartz提供了多种`misfire_instr`对应的处理策略,例如`MISFIRE_INSTRUCTION_DO_NOTHING`,该策略会忽略失火情况,相当于失火没有发生过一样,具体表现为用当前日期再次计算下次触发时间(考虑`Calendar`的限制),当前时间并不触发任务[^3]。 另外,当重复计数为`REPEAT_INDEFINITELY`时,指令将解释为`MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT`。不过需要注意的是,如果触发器具有非空的结束时间,使用`MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT`可能会导致触发器在失火时间范围内到达结束时间时,不会再次触发[^4]。 以下是一个简单的Java代码示例,展示如何设置`misfire`策略: ```java import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; public class QuartzMisfireExample { public static void main(String[] args) throws SchedulerException { // 创建调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 定义一个Job JobDetail job = newJob(MyJob.class) .withIdentity("myJob", "group1") .build(); // 定义一个触发器,并设置misfire策略 Trigger trigger = newTrigger() .withIdentity("myTrigger", "group1") .withSchedule(cronSchedule("0 0 12 * * ?") .withMisfireHandlingInstructionFireAndProceed()) // 设置misfire策略 .build(); // 将Job和触发器添加到调度器 scheduler.scheduleJob(job, trigger); // 启动调度器 scheduler.start(); } public static class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("Job executed!"); } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值