Quartz – Misfire (for RAMJobstore)

Quartz – Misfire (for RAMJobstore)JAVA任务调度定时任务QuartzMisfireforRAMJobstor_misfire_instruction_smart_policy

Quartz - Misfire (for RAMJobstore)

Misfire,看到很多文章翻译为“失火”,其实感觉这个翻译和他本身代表的含义差距比较大。

Quartz中的fire表示触发器被触发的意思,Misfire则表示本来应该到了触发器被触发的时间,但是实际却没有触发,是“错过触发”的意思。

#### Misfire的原因

触发器Misfire的原因大概有以下几种:

1. 触发器在初始化时设置的start时间小于当前系统时间

2. jobStore设置为JDBC(通过数据库持久化存储),系统down机后再次启动

3. 任务调度线程阻塞

4. 没有可用任务执行线程(被其他正在执行中的任务占满)

#### Misfire的处理策略

发生Misfire之后,Quartz的不同Trigger会提供不同的处理策略。其中SimplerTrigger和CronTrigger的共同处理策略是:

1. MISFIRE_INSTRUCTION_SMART_POLICY:smart policy,其实最终实现取决于Trigger实现类

2. MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:忽略Misfire就当Misfire没有发生,意思是任务调度器错过了某一触发器的触发时间之后,一旦该触发器获得执行机会后,会把所有错过触发的时间补回来。比如simpleTrigger设置为每15秒执行一次,由于某种原因在最近5分钟内错过了触发,则一旦其获得执行机会后,会连续触发5*(60/15)=20次

SimplerTrigger的处理策略包括:

1. MISFIRE_INSTRUCTION_FIRE_NOW:对于一次性任务则立即触发,对于设置了执行次数repeat count的循环执行的任务,则等同于MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT。

2. MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT:立即触发(不考虑Calendar的限制),而且触发次数不受影响(即使Misfre也能保证触发次数),比如设置执行100次,已执行10次,Misfire了5次,则剩余执行次数为100-10=90次(***我们称之为触发次数不受影响***)。但是如果同时设置了EndTime,则需要遵守EndTime的限制(如果当前时间已经超过的EndTime则不再触发)。

3. MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT:立即触发(不考虑Calendar的限制),但是***触发次数会受到影响***:将Misfire的次数也考虑在内,比如设置为执行100次,已执行10次,Misfire了5次,则剩余次数为100-(10+5)=85次,所以错过的5次实际上还是被错过了。而且需要遵守EndTime的约定,如果当前时间已经超过的EndTime则不再触发。

4. MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT:立即触发(考虑Calendar的限制),***触发次数会受到影响***,需要遵守EndTime的限制。

5. MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT:立即触发(考虑Calendar的限制),需遵守EndTime的限制,***触发次数不受影响。***

6. MISFIRE_INSTRUCTION_SMART_POLICY:默认处理策略,如果是不重复(只触发一次)触发器,等同于MISFIRE_INSTRUCTION_FIRE_NOW,如果是触发无数次的(永不停止)触发器,等同于MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT,否则如果是触发有限次数的触发器,等同于MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT。

CronTrigger的处理策略:

1. MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:立即触发。

2. MISFIRE_INSTRUCTION_DO_NOTHING:忽略Misfire就相当于Misfire没有发生过一样,具体来说就是用当前日期再次计算下次触发时间(考虑Calendar的限制),当前时间并不触发。

3. MISFIRE_INSTRUCTION_SMART_POLICY:默认的处理策略,等同于MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。

#### Misfire处理策略总结

Misfire处理策略看起来非常复杂,尤其是SimpleTrigger的策略,但是一般情况下我们并不需要这么复杂的Misfire策略,所以绝大部分情况我们只需要使用默认的策略就可以。

特殊情况下,比如严格要求到EndTime必须执行够多少次这类需求,就需要我们认真研究不同策略的区别,采用合适的Misfire策略才能确保满足需求。

不管是SimplerTrigger还是CronTrigger,默认的Misfire处理策略都是MISFIRE_INSTRUCTION_SMART_POLICY,所谓smart policy其实就是可以根据Trigger的不同特性做出不同的处理。

#### Misfire的设置

触发器创建的时候通过TriggerBuilder的ScheduleBuilder指定,SimpleSchduleBuilder对应设置SimpleTrigger的Misfire处理策略,CronScheduleBuilder对应设置CronTrigger的Misfire策略。

“`

Trigger trigger = newTrigger()

.withIdentity(“myTriggger”,”MyGroup”)

.startNow()

.withSchedule(simpleSchedule()

.withIntervalInSeconds(20)

.withMisfireHandlingInstructionFireNow())

.build();

“`

#### Misfire处理策略源码分析

任务调度线程QuartzSchedulerThread的run方法我们前面已经简单做过分析,Quartz的任务调度就是在这里进行的。其中会调用JobStore的acquireNextTriggers方法获取在idleWaitTime时间范围内需要被调度的Trigger。

以RAMJobStore为例,acquireNextTriggers方法会逐一获取timeTriggers中的Trigger,之后首先调用applyMisfire(tw)进行Misfire处理。

applyMisfire(tw)方法首先判断Trigger的Misfire策略如果是IGNORE_MISFIRE_POLICY,或者Trigger的触发时间大于系统时间减去系统设置的Misfire容忍时间,则不认为是Misfire,返回false。否则会认为是Misfire,进行Misfire时候的后续处理。

“`

long misfireTime = System.currentTimeMillis();

if (getMisfireThreshold() > 0) {

misfireTime -= getMisfireThreshold();

}

Date tnft = tw.trigger.getNextFireTime();

if (tnft == null || tnft.getTime() > misfireTime

|| tw.trigger.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {

return false;

}

“`

这里需要注意的是第二个条件,“Trigger的触发时间大于系统时间减去系统设置的Misfire容忍时间”,我们需要举例简单分析一下。

假设我们设置的Misfire容忍时间是60秒,Trigger的触发时间为7点55分整。

如果当前系统时间是7点56,减去容忍时间60秒之后(我们暂时称之为Misfire时间)是7点55,***Trigger的触发时间7点55分并不大于Misfire时间,系统认为发生了Misfire。而且当前时间如果大于7点56分的话,都会认为是发生了Misfire。***

如果当前时间是7点55分59秒,减去容忍时间60秒之后的Misfire时间是7点54分59秒,***Trigger的触发时间7点55分大于Misfire时间,所以系统认为没有发生Misfire。

如果发生了Misfire,调用trigger的updateAfterMisfire方法进行发生Misfire后的处理,Misfire处理策略就是在这个updateAfterMisfire方法中生效的,updateAfterMisfire方法的处理逻辑体现了不同Trigger的Misfire处理策略。

updateAfterMisfire的处理逻辑其实就是Trigger的不同Misfire处理策略的实现过程,研究updateAfterMisfire方法源码是理解Misfire处理策略的最好的办法。

如果applyMisfire(tw)方法返回true,也就是说Quartz认为发生了Misfire,则系统判断经过Misfire处理之后的Trigger的下次触发时间不为空的话,就会把当前trigger重新加回到timeTriggers中,继续执行下次循环。

“`

if (applyMisfire(tw)) {

if (tw.trigger.getNextFireTime() != null) {

timeTriggers.add(tw);

}

continue;

}

“`

其实我们分析Trigger的不同Misfire策略后会发现,只要发生了Misfire、而且在Misfire处理之后Trigger的下次执行时间不为空,则不管是什么Misfire策略均表示要在当前时间触发该Trigger。

既然要在当前时间触发该Trigger,为什么还要把Trigger放回timeTriggers、而不是直接执行该Trigger?个人认为原因是,放回去重新排序,因为timeTrigger是一个有序队列,其中很可能会有触发时间比当前时间更早的其他触发器,应该在触发当前这个Misfired的Trigger之前被触发。

Over!

上一篇 [Quartz – Trigger & RAMJobStore](https://segmentfault.com/a/1190000043391831)

下一篇 [easypoi 模板导出时的公式及foreach合并单元格问题](https://segmentfault.com/a/1190000043415590)

今天的文章Quartz – Misfire (for RAMJobstore)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89141.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注