作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?
2013-03-11 15:23
513 查看
不知道大家在用Quartz的时候 有没有遇到这样一种情况:
触发器设定每3秒钟触发一次 ,但是工作需要10秒钟的执行时间.因此,在一次任务结束执行前,触发器已经错失触发
当这种情况下我们怎么处理呢,让我们一起学习一下......
还是先贴代码:
job类:StatefulDumbJob.java
调度类: MisfireExample.java
-------------------------------------我是分割线----------------------------------------------------------
先说明 一个词 :
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的线程池中的线程数时,也会发生类似的情况 兄弟们可以参考一下下面的这篇博文:
/article/5758269.html
触发器设定每3秒钟触发一次 ,但是工作需要10秒钟的执行时间.因此,在一次任务结束执行前,触发器已经错失触发
当这种情况下我们怎么处理呢,让我们一起学习一下......
还是先贴代码:
job类:StatefulDumbJob.java
import java.text.SimpleDateFormat; import java.util.Calendar; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; @PersistJobDataAfterExecution @DisallowConcurrentExecution public class StatefulDumbJob implements Job { // 静态常量,作为任务在调用间,保持数据的键(key) // NUM_EXECUTIONS,保存的计数每次递增1 // EXECUTION_DELAY,任务在执行时,中间睡眠的时间。本例中睡眠时间过长导致了错失触发 public static final String NUM_EXECUTIONS = "NumExecutions"; public static final String EXECUTION_DELAY = "ExecutionDelay"; @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 任务执行的时间 SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); String jobRunTime = dateFormat.format(Calendar.getInstance().getTime()); System.err.println("---" + context.getJobDetail().getKey().getName() + " 在 : [" + jobRunTime + "] 执行了!!"); // 任务执行计数 累加 JobDataMap map = context.getJobDetail().getJobDataMap(); int executeCount = 0; if (map.containsKey(NUM_EXECUTIONS)) { executeCount = map.getInt(NUM_EXECUTIONS); } executeCount++; map.put(NUM_EXECUTIONS, executeCount); // 睡眠时间: 由调度类重新设置值 ,本例为 睡眠10s long delay = 5000l; if (map.containsKey(EXECUTION_DELAY)) { delay = map.getLong(EXECUTION_DELAY); } try { Thread.sleep(delay); } catch (Exception ignore) { } // 睡眠醒来后,打印任务执行结束的信息 System.err.println(" -" + context.getJobDetail().getKey().getName() + " 完成次数 : " + executeCount ); } }
调度类: MisfireExample.java
import static org.quartz.DateBuilder.nextGivenSecondDate; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import java.text.SimpleDateFormat; import java.util.Date; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SchedulerMetaData; import org.quartz.SimpleTrigger; import org.quartz.impl.StdSchedulerFactory; public class MisfireExample { public static void main(String[] args) throws Exception { MisfireExample example = new MisfireExample(); example.run(); } public void run() throws Exception { // 任务执行的时间 格式化 SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); System.out.println("--------------- 初始化 -------------------"); // 下一个第15秒 Date startTime = nextGivenSecondDate(null, 15); // statefulJob1 每3s运行一次,但它会延迟10s JobDetail job = newJob(StatefulDumbJob.class) .withIdentity("statefulJob1", "group1") .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L) // 设置参数:睡眠时间 10s .build(); SimpleTrigger trigger = newTrigger() .withIdentity("trigger1", "group1") .startAt(startTime) .withSchedule( simpleSchedule().withIntervalInSeconds(3) .repeatForever()).build(); Date ft = sched.scheduleJob(job, trigger); System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft) + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 " + trigger.getRepeatInterval() / 1000 + " 秒"); // statefulJob2 将每3s运行一次 , 但它将延迟10s , 然后不断的迭代 job = newJob(StatefulDumbJob.class) .withIdentity("statefulJob2", "group1") .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)// 设置参数:睡眠时间 10s .build(); trigger = newTrigger() .withIdentity("trigger2", "group1") .startAt(startTime) .withSchedule( simpleSchedule() .withIntervalInSeconds(3) .repeatForever() // 设置错失触发后的调度策略 .withMisfireHandlingInstructionNowWithRemainingCount() ) .build(); ft = sched.scheduleJob(job, trigger); System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft) + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 " + trigger.getRepeatInterval() / 1000 + " 秒"); System.out.println("------- 开始调度 (调用.start()方法) ----------------"); sched.start(); // 给任务留时间运行 Thread.sleep(600L * 1000L); sched.shutdown(true); System.out.println("------- 调度已关闭 ---------------------"); // 显示一下 已经执行的任务信息 SchedulerMetaData metaData = sched.getMetaData(); System.out.println("~~~~~~~~~~ 执行了 " + metaData.getNumberOfJobsExecuted() + " 个 jobs."); } }
-------------------------------------我是分割线----------------------------------------------------------
先说明 一个词 :
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的线程池中的线程数时,也会发生类似的情况 兄弟们可以参考一下下面的这篇博文:
/article/5758269.html
相关文章推荐
- 作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?
- 作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?
- 作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?
- 作业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?
- 作业调度框架 Quartz 学习笔记(三) -- Cron表达式
- 任务调度框架Quartz学习笔记—概述及第一个例子
- 作业调度框架 Quartz 学习笔记(六) -- job生病了(抛出异常)时的处理
- 作业调度框架 Quartz 学习笔记(四) -- 接收参数和维护状态
- 作业调度框架 Quartz 学习笔记(六) -- job生病了(抛出异常)时的处理
- 作业调度框架 Quartz 学习笔记(六) -- job生病了(抛出异常)时的处理
- Quartz.NET(作业调度框架) 学习笔记(一)【了解作业调度框架】
- Quartz.NET(作业调度框架) 学习笔记(二)【Hello Job】
- 作业调度框架 Quartz 学习笔记(四) -- 接收参数和维护状态
- 作业调度框架 Quartz 学习笔记
- 作业调度框架 Quartz 学习笔记(一) -- HelloWorld !!!
- Quartz.NET(作业调度框架) 学习笔记(三)【Cron 表达式】
- 作业调度框架 Quartz 学习笔记(一) -- HelloWorld !!!
- 作业调度框架 Quartz 学习笔记(二) -- 简单触发器(SimpleTrigger)
- 作业调度框架 Quartz 学习笔记(一) -- HelloWorld !!!
- 作业调度框架 Quartz 学习笔记(四) -- 接收参数和维护状态