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