作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办? 落日映苍穹つ 2022-05-29 01:19 136阅读 0赞 非常感谢[http://blog.csdn.net/lnara/article/details/8658258][http_blog.csdn.net_lnara_article_details_8658258] 不知道大家在用Quartz的时候 有没有遇到这样一种情况: 触发器设定每3秒钟触发一次 ,但是工作需要10秒钟的执行时间.因此,在一次任务结束执行前,触发器已经错失触发 当这种情况下我们怎么处理呢,让我们一起学习一下...... 还是先贴代码: job类:StatefulDumbJob.java **\[java\]** [view plain][] [copy][view plain] 1. import java.text.SimpleDateFormat; 2. import java.util.Calendar; 3. import org.quartz.DisallowConcurrentExecution; 4. import org.quartz.Job; 5. import org.quartz.JobDataMap; 6. import org.quartz.JobExecutionContext; 7. import org.quartz.JobExecutionException; 8. import org.quartz.PersistJobDataAfterExecution; 9. 10. @PersistJobDataAfterExecution 11. @DisallowConcurrentExecution 12. public class StatefulDumbJob implements Job \{ 13. 14. // 静态常量,作为任务在调用间,保持数据的键(key) 15. // NUM\_EXECUTIONS,保存的计数每次递增1 16. // EXECUTION\_DELAY,任务在执行时,中间睡眠的时间。本例中睡眠时间过长导致了错失触发 17. public static final String NUM\_EXECUTIONS = "NumExecutions"; 18. public static final String EXECUTION\_DELAY = "ExecutionDelay"; 19. 20. @Override 21. public void execute(JobExecutionContext context) 22. throws JobExecutionException \{ 23. 24. // 任务执行的时间 25. SimpleDateFormat dateFormat = new SimpleDateFormat( 26. "yyyy-MM-dd HH:mm:ss"); 27. String jobRunTime = dateFormat.format(Calendar.getInstance().getTime()); 28. 29. System.err.println("---" + context.getJobDetail().getKey().getName() + " 在 : \[" 30. + jobRunTime + "\] 执行了!!"); 31. 32. // 任务执行计数 累加 33. JobDataMap map = context.getJobDetail().getJobDataMap(); 34. int executeCount = 0; 35. if (map.containsKey(NUM\_EXECUTIONS)) \{ 36. executeCount = map.getInt(NUM\_EXECUTIONS); 37. \} 38. executeCount++; 39. map.put(NUM\_EXECUTIONS, executeCount); 40. 41. // 睡眠时间: 由调度类重新设置值 ,本例为 睡眠10s 42. long delay = 5000l; 43. if (map.containsKey(EXECUTION\_DELAY)) \{ 44. delay = map.getLong(EXECUTION\_DELAY); 45. \} 46. 47. try \{ 48. Thread.sleep(delay); 49. \} catch (Exception ignore) \{ 50. \} 51. 52. // 睡眠醒来后,打印任务执行结束的信息 53. System.err.println(" -" + context.getJobDetail().getKey().getName() 54. + " 完成次数 : " + executeCount ); 55. \} 56. \} 调度类: MisfireExample.java **\[java\]** [view plain][] [copy][view plain] 1. import static org.quartz.DateBuilder.nextGivenSecondDate; 2. import static org.quartz.JobBuilder.newJob; 3. import static org.quartz.SimpleScheduleBuilder.simpleSchedule; 4. import static org.quartz.TriggerBuilder.newTrigger; 5. 6. import java.text.SimpleDateFormat; 7. import java.util.Date; 8. 9. import org.quartz.JobDetail; 10. import org.quartz.Scheduler; 11. import org.quartz.SchedulerFactory; 12. import org.quartz.SchedulerMetaData; 13. import org.quartz.SimpleTrigger; 14. import org.quartz.impl.StdSchedulerFactory; 15. 16. public class MisfireExample \{ 17. 18. public static void main(String\[\] args) throws Exception \{ 19. MisfireExample example = new MisfireExample(); 20. example.run(); 21. \} 22. 23. public void run() throws Exception \{ 24. // 任务执行的时间 格式化 25. SimpleDateFormat dateFormat = new SimpleDateFormat( 26. "yyyy-MM-dd HH:mm:ss"); 27. 28. SchedulerFactory sf = new StdSchedulerFactory(); 29. Scheduler sched = sf.getScheduler(); 30. System.out.println("--------------- 初始化 -------------------"); 31. 32. // 下一个第15秒 33. Date startTime = nextGivenSecondDate(null, 15); 34. 35. // statefulJob1 每3s运行一次,但它会延迟10s 36. JobDetail job = newJob(StatefulDumbJob.class) 37. .withIdentity("statefulJob1", "group1") 38. .usingJobData(StatefulDumbJob.EXECUTION\_DELAY, 10000L) // 设置参数:睡眠时间 10s 39. .build(); 40. SimpleTrigger trigger = newTrigger() 41. .withIdentity("trigger1", "group1") 42. .startAt(startTime) 43. .withSchedule( 44. simpleSchedule().withIntervalInSeconds(3) 45. .repeatForever()).build(); 46. Date ft = sched.scheduleJob(job, trigger); 47. System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft) 48. + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 " 49. + trigger.getRepeatInterval() / 1000 + " 秒"); 50. 51. // statefulJob2 将每3s运行一次 , 但它将延迟10s , 然后不断的迭代 52. job = newJob(StatefulDumbJob.class) 53. .withIdentity("statefulJob2", "group1") 54. .usingJobData(StatefulDumbJob.EXECUTION\_DELAY, 10000L)// 设置参数:睡眠时间 10s 55. .build(); 56. trigger = newTrigger() 57. .withIdentity("trigger2", "group1") 58. .startAt(startTime) 59. .withSchedule( 60. simpleSchedule() 61. .withIntervalInSeconds(3) 62. .repeatForever() 63. // 设置错失触发后的调度策略 64. .withMisfireHandlingInstructionNowWithRemainingCount() 65. ) 66. .build(); 67. 68. ft = sched.scheduleJob(job, trigger); 69. System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft) 70. + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 " 71. + trigger.getRepeatInterval() / 1000 + " 秒"); 72. 73. System.out.println("------- 开始调度 (调用.start()方法) ----------------"); 74. sched.start(); 75. 76. // 给任务留时间运行 77. Thread.sleep(600L \* 1000L); 78. 79. sched.shutdown(true); 80. System.out.println("------- 调度已关闭 ---------------------"); 81. 82. // 显示一下 已经执行的任务信息 83. SchedulerMetaData metaData = sched.getMetaData(); 84. System.out.println("~~~~~~~~~~ 执行了 " 85. + metaData.getNumberOfJobsExecuted() + " 个 jobs."); 86. \} 87. \} \-------------------------------------我是分割线---------------------------------------------------------- 先说明 一个词 : misfire -- 指的是 错过了触发时间 你会注意到2个触发器具有相同的时间安排,相同的任务 触发器设定每3秒钟触发一次,但是工作需要10秒钟的执行时间 因此,在一次任务结束执行前,触发器已经错失触发(除非’错失触发时限’被设置为超过7秒)。 其中第二个任务设置自己的错失触发指示:.withMisfireHandlingInstructionNowWithRemainingCount() 所以当检测到丢失触发时,不会立即触发,而是忽略本次安排到下一个预定时间去触发 我们的结论 : a) 本范例中,触发的间隔被设置为3秒,但是由于任务体执行间睡眠10秒,导致了错失触发的产生。 b) 实际运行的效果,2个任务执行的间隔为10秒。 c) 由于丢失触发时,job2的策略是立即触发,而job1是等待下一次机会触发。所以job2会赶在job1的前头,最终运行次数大于job1。 Quartz 的 Misfire处理规则: 调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不同的misfire对应的处理规则 CronTrigger withMisfireHandlingInstructionDoNothing ——不触发立即执行 ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行 withMisfireHandlingInstructionIgnoreMisfires ——以错过的第一个频率时间立刻开始执行 ——重做错过的所有频率周期后 ——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行 withMisfireHandlingInstructionFireAndProceed ——以当前时间为触发频率立刻触发一次执行 ——然后按照Cron频率依次执行 SimpleTrigger withMisfireHandlingInstructionFireNow ——以当前时间为触发频率立即触发执行 ——执行至FinalTIme的剩余周期次数 ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到 ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值 withMisfireHandlingInstructionIgnoreMisfires ——以错过的第一个频率时间立刻开始执行 ——重做错过的所有频率周期 ——当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率 ——共执行RepeatCount+1次 withMisfireHandlingInstructionNextWithExistingCount ——不触发立即执行 ——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数 ——以startTime为基准计算周期频率,并得到FinalTime ——即使中间出现pause,resume以后保持FinalTime时间不变 withMisfireHandlingInstructionNowWithExistingCount ——以当前时间为触发频率立即触发执行 ——执行至FinalTIme的剩余周期次数 ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到 ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值 withMisfireHandlingInstructionNextWithRemainingCount ——不触发立即执行 ——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数 ——以startTime为基准计算周期频率,并得到FinalTime ——即使中间出现pause,resume以后保持FinalTime时间不变 withMisfireHandlingInstructionNowWithRemainingCount ——以当前时间为触发频率立即触发执行 ——执行至FinalTIme的剩余周期次数 ——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到 ——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值 MISFIRE\_INSTRUCTION\_RESCHEDULE\_NOW\_WITH\_REMAINING\_REPEAT\_COUNT ——此指令导致trigger忘记原始设置的starttime和repeat-count ——触发器的repeat-count将被设置为剩余的次数 ——这样会导致后面无法获得原始设定的starttime和repeat-count值 另外,如果任务数超过了Quartz的线程池中的线程数时,也会发生类似的情况 兄弟们可以参考一下下面的这篇博文: [http://www.cnblogs.com/makemelaugh/archive/2012/06/17/2533105.html][http_www.cnblogs.com_makemelaugh_archive_2012_06_17_2533105.html] [http_blog.csdn.net_lnara_article_details_8658258]: http://blog.csdn.net/lnara/article/details/8658258 [view plain]: http://blog.csdn.net/lnara/article/details/8658258# [http_www.cnblogs.com_makemelaugh_archive_2012_06_17_2533105.html]: http://www.cnblogs.com/makemelaugh/archive/2012/06/17/2533105.html
还没有评论,来说两句吧...