調度(scheduleJob)或恢復調度(resumeTrigger,resumeJob)后不同的misfire對應的處理規則
misfire產生的條件是:到了該觸發執行時上一個執行還未完成,且線程池中沒有空閑線程可以使用(或有空閑線程可以使用但job設置為@DisallowConcurrentExecution)且過期時間已經超過misfireThreshold就認為是misfire了,錯失觸發了
比如:13:07:24開始執行,重復執行5次,開始執行時,quartz已經計算好每次調度的時間刻,分別如下:
03:33:36,03:33:39,03:33:42,03:33:45,03:33:48,03:33:51
如果第一次執行時間為11s,到03:33:47結束,03:33:47減去03:33:39的時間間隔是8s,如果misfireThreshold設置的時間小於等於8s間隔,則認為是misfire了,如果大於8s間隔,則認為沒有misfire。
CronTrigger
CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
csb.withMisfireHandlingInstructionDoNothing();
csb.withMisfireHandlingInstructionFireAndProceed();(默認)
csb.withMisfireHandlingInstructionIgnoreMisfires();
withMisfireHandlingInstructionDoNothing ——不觸發立即執行 ——等待下次Cron觸發頻率到達時刻開始按照Cron頻率依次執行
withMisfireHandlingInstructionIgnoreMisfires ——以錯過的第一個頻率時間立刻開始執行 ——重做錯過的所有頻率周期后 ——當下一次觸發頻率發生時間大於當前時間后,再按照正常的Cron頻率依次執行
withMisfireHandlingInstructionFireAndProceed(默認) ——以當前時間為觸發頻率立刻觸發一次執行 ——然后按照Cron頻率依次執行
SimpleTrigger
SimpleScheduleBuilder ssb = SimpleScheduleBuilder.simpleSchedule(); ssb.withMisfireHandlingInstructionFireNow(); ssb.withMisfireHandlingInstructionIgnoreMisfires(); ssb.withMisfireHandlingInstructionNextWithExistingCount(); ssb.withMisfireHandlingInstructionNextWithRemainingCount(); ssb.withMisfireHandlingInstructionNowWithExistingCount(); (默認) ssb.withMisfireHandlingInstructionNowWithRemainingCount();
withMisfireHandlingInstructionFireNow ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires ——以錯過的第一個頻率時間立刻開始執行 ——重做錯過的所有頻率周期 ——當下一次觸發頻率發生時間大於當前時間以后,按照Interval的依次執行剩下的頻率 ——共執行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount ——不觸發立即執行 ——等待下次觸發頻率周期時刻,執行至FinalTime的剩余周期次數 ——以startTime為基准計算周期頻率,並得到FinalTime ——即使中間出現pause,resume以后保持FinalTime時間不變
withMisfireHandlingInstructionNextWithRemainingCount ——不觸發立即執行 ——等待下次觸發頻率周期時刻,執行至FinalTime的剩余周期次數 ——以startTime為基准計算周期頻率,並得到FinalTime ——即使中間出現pause,resume以后保持FinalTime時間不變
withMisfireHandlingInstructionNowWithExistingCount(默認) ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值
withMisfireHandlingInstructionNowWithRemainingCount ——以當前時間為觸發頻率立即觸發執行 ——執行至FinalTIme的剩余周期次數 ——以調度或恢復調度的時刻為基准的周期頻率,FinalTime根據剩余次數和當前時間計算得到 ——調整后的FinalTime會略大於根據starttime計算的到的FinalTime值 MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT ——此指令導致trigger忘記原始設置的starttime和repeat-count ——觸發器的repeat-count將被設置為剩余的次數 ——這樣會導致后面無法獲得原始設定的starttime和repeat-count值
misfireHandler線程
下面這些原因可能造成 misfired job:
- 系統因為某些原因被重啟。在系統關閉到重新啟動之間的一段時間里,可能有些任務會被 misfire;
- Trigger 被暫停(suspend)的一段時間里,有些任務可能會被 misfire;
- 線程池中所有線程都被占用,導致任務無法被觸發執行,造成 misfire;
- 有狀態任務在下次觸發時間到達時,上次執行還沒有結束;為了處理 misfired job,Quartz 中為 trigger 定義了處理策略,主要有下面兩種:MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:針對 misfired job 馬上執行一次;MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次觸發;默認是MISFIRE_INSTRUCTION_SMART_POLICY,該策略在CronTrigger中=MISFIRE_INSTRUCTION_FIRE_ONCE_NOW線程默認1分鍾執行一次;在一個事務中,默認一次最多recovery 20個;
執行流程:
- 若配置(默認為true,可配置)成獲取鎖前先檢查是否有需要recovery的trigger,先獲取misfireCount;
- 獲取TRIGGER_ACCESS鎖;
- hasMisfiredTriggersInState:獲取misfired的trigger,默認一個事務里只能最大20個misfired trigger(可配置),misfired判斷依據:status=waiting,next_fire_time < current_time-misfirethreshold(可配置,默認1min)
- notifyTriggerListenersMisfired
- updateAfterMisfire:獲取misfire策略(默認是MISFIRE_INSTRUCTION_SMART_POLICY,該策略在CronTrigger中=MISFIRE_INSTRUCTION_FIRE_ONCE_NOW),根據策略更新nextFireTime;
- 將nextFireTime等更新到trigger表;
- commit connection,釋放鎖
- 如果還有更多的misfired,sleep短暫時間(為了集群負載均衡),否則sleep misfirethreshold時間,后繼續輪詢;
misfireHandler線程執行流程如下圖所示:

