当前位置: 首页 > 后端技术 > Java

Quartz-Misfire(forRAMJobstore)

时间:2023-04-01 17:17:11 Java

Misfire,看到很多文章都翻译成“misfire”,其实感觉这个翻译和它所代表的意思大相径庭。Quartz中的Fire是指trigger被触发了,Misfire是指应该触发了trigger,但实际上并没有触发,意思是“错过了trigger”。Misfire的原因触发器Misfire的原因大概有以下几种:初始化时触发器设置的启动时间小于当前系统时间jobStore设置为JDBC(通过数据库持久化存储),任务调度线程阻塞当系统宕机重启时可用的任务执行线程(被其他正在执行的任务占用)Misfire处理策略Misfire发生后,Quartz的不同Trigger会提供不同的处理策略。其中SimplerTrigger和CronTrigger共同的处理策略是:MISFIRE_INSTRUCTION_SMART_POLICY:smartpolicy,其实最终实现依赖于Trigger实现类MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:IgnoreMisfire,假设Misfire没有发生,意思是taskschedulermiss之后某个trigger的触发时间,一旦trigger获得执行机会后,就会补齐所有错过的trigger次数。例如,simpleTrigger设置为每15秒执行一次。由于某种原因,它在最后5分钟内错过了触发。一旦有机会执行,就会连续触发5*(60/15)=20次。SimplerTrigger的处理策略包括:MISFIRE_INSTRUCTION_FIRE_NOW:对于一次性任务立即触发,相当于MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT对于设置了重复次数的循环执行任务。MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT:立即触发(不受Calendar限制),触发次数不受影响(即使是Misfre也能保证触发次数)。比如设置执行了100次,已经执行了10次,Misfire执行了5次,那么剩余的执行次数就是100-10=90次(我们称之为不受影响的触发次数)。但如果同时设置了EndTime,则需要遵守EndTime的限制(如果当前时间已经超过EndTime,则不再触发)。MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT:立即触发(不考虑Calendar限制),但触发次数会受影响:考虑Misfires次数,比如设置执行100次,已经执行10次,Misfired5次,那么剩下的次数就是100-(10+5)=85次,所以错过的5次其实还是错过了。并且需要遵守EndTime约定,如果当前时间已经超过了EndTime,则不再触发。MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT:立即触发(考虑到Calendar的限制),触发次数会受到影响,需要遵守EndTime的限制。MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT:立即触发(考虑Calendar限制),受EndTime限制,触发次数不受影响。MISFIRE_INSTRUCTION_SMART_POLICY:默认处理策略,如果是不重复触发(只触发一次),相当于MISFIRE_INSTRUCTION_FIRE_NOW,如果是触发无数次(永不停止),相当于MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT,否则如果它是一个触发次数有限的触发器,相当于MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT。CronTrigger的处理策略:MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:立即触发。MISFIRE_INSTRUCTION_DO_NOTHING:忽略Misfire等同于Misfire没有发生。具体是用当前日期重新计算下一次触发时间(考虑到Calendar的限制),当前时间不触发。MISFIRE_INSTRUCTION_SMART_POLICY:默认的处理策略,相当于MISFIRE_INSTRUCTION_FIRE_ONCE_NOW。Misfire处理策略总结Misfire处理策略看似很复杂,尤其是SimpleTrigger策略,但一般情况下我们不需要这么复杂的Misfire策略,所以大多数情况下我们只需要使用默认策略即可。在特殊情况下,比如对EndTime必须执行多少次有严格要求,我们需要仔细研究不同策略之间的差异,采用合适的Misfire策略来保证满足要??求。不管SimplerTrigger还是CronTrigger,Misfire默认的处理策略都是MISFIRE_INSTRUCTION_SMART_POLICY。所谓smartpolicy其实可以根据Trigger的不同特性做不同的处理。Misfiresettings触发器创建时,由TriggerBuilder的ScheduleBuilder指定。SimpleSchduleBuilder对应设置SimpleTrigger的Misfire处理策略,CronScheduleBuilder对应设置CronTrigger的Misfire策略。触发器trigger=newTrigger().withIdentity("myTrigger","MyGroup").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(20).withMisfireHandlingInstructionFireNow()).build();Misfire处理策略源码分析任务调度线程前面已经简单分析了QuartzSchedulerThread的run方法,这里就是进行Quartz的任务调度。会调用JobStore的acquireNextTriggers方法获取idleWaitTime时间段内需要调度的Triggers。以RAMJobStore为例,acquireNextTriggers方法会逐个获取timeTriggers中的Triggers,然后先调用applyMisfire(tw)进行Misfire处理。applyMisfire(tw)方法首先判断如果Trigger的Misfire策略为IGNORE_MISFIRE_POLICY,或者Trigger的触发时间大于系统时间减去系统设置的Misfire容忍时间,则不认为是Misfire并返回错误的。否则视为Misfire,进行Misfire的后续处理。longmisfireTime=System.currentTimeMillis();如果(getMisfireThreshold()>0){misfireTime-=getMisfireThreshold();}日期tnft=tw.trigger.getNextFireTime();如果(tnft==null||tnft.getTime()>misfireTime||tw.trigger.getMisfireInstruction()==Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY){returnfalse;}这里需要注意的是第二个条件,“Trigger的触发时间大于系统时间减去系统设置的Misfiretolerance时间”,我们需要结合例子来简单分析一下。假设我们将Misfire的容忍时间设置为60秒,Trigger的触发时间为7:55。如果当前系统时间为7:56,减去60秒的容忍时间(我们暂称它为Misfire时间)为7:55,且7:55的Trigger时间不大于Misfire时间,系统认为发生失火。如果当前时间大于7:56,则认为发生了Misfire。如果当前时间为7:55:59,减去60秒的容差时间后的Misfire时间为7:54:59,7:55*Trigger的触发时间大于Misfire时间,所以系统认为没有发生Misfire。如果发生Misfire,则在Misfire发生后调用trigger的updateAfterMisfire方法进行处理。Misfire处理策略在这个updateAfterMisfire方法中生效。updateAfterMisfire方法的处理逻辑体现了不同Trigger的Misfire处理策略。updateAfterMisfire的处理逻辑其实就是Trigger对不同Misfire处理策略的执行过程。研究updateAfterMisfire方法的源码是理解Misfire处理策略的最好途径。如果applyMisfire(tw)方法返回true,也就是说Quartz认为发生了Misfire,系统判断Misfire处理后的Trigger下一次触发时间不为空,会把当前的trigger加回timeTriggers并继续执行下一个循环。如果(applyMisfire(tw)){如果(tw.trigger.getNextFireTime()!=null){timeTriggers.add(tw);}继续;其实我们分析完Trigger的不同Misfire策略后会发现,只要是Misfire,并且经过Misfire处理后,Trigger的下一次执行时间就不为空,所以不管Misfire策略是什么,都意味着触发器应在当前时间触发。既然Trigger是要在当前时间触发的,那为什么要把Trigger放回timeTriggers而不是直接执行Trigger呢?我个人认为原因是放回去重新排序,因为timeTrigger是一个有序队列,很可能还有其他触发时间早于当前时间的触发器,应该在当前MisfiredTrigger之前触发触发。超过!上一篇Quartz-Trigger&RAMJobStore下一篇导出easypoi模板时的公式和foreach合并单元格问题