做业调度框架 Quartz 学习笔记(五) -- 错过的任务怎么办?

不知道你们在用Quartz的时候 有没有遇到这样一种状况:html

触发器设定每3秒钟触发一次 ,可是工做须要10秒钟的执行时间.所以,在一次任务结束执行前,触发器已经错失触发java

当这种状况下咱们怎么处理呢,让咱们一块儿学习一下......ide

仍是先贴代码:学习

job类:StatefulDumbJob.javaui

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhubo
 * Date: 2017-10-24
 * Time: 17:25
 */
@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 e){

        }
        // 睡眠醒来后,打印任务执行结束的信息
        System.err.println("  -" + context.getJobDetail().getKey().getName()
                + " 完成次数  : " + executeCount );

    }
}

调度类: MisfireExample.javaspa

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 -- 指的是 错过了触发时间 code

你会注意到2个触发器具备相同的时间安排,相同的任务orm

触发器设定每3秒钟触发一次,可是工做须要10秒钟的执行时间htm

所以,在一次任务结束执行前,触发器已经错失触发(除非错失触发时限被设置为超过7)

其中第二个任务设置本身的错失触发指示:.withMisfireHandlingInstructionNowWithRemainingCount()

  因此当检测到丢失触发时,不会当即触发,而是忽略本次安排到下一个预约时间去触发

咱们的结论 :

a)       本范例中,触发的间隔被设置为3秒,可是因为任务体执行间睡眠10秒,致使了错失触发的产生。

b)       实际运行的效果,2个任务执行的间隔为10秒。

c)        因为丢失触发时,job2的策略是当即触发,而job1是等待下一次机会触发。因此job2会赶在job1的前头,最终运行次数大于job1

Quartz 的 Misfire处理规则:

 调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不一样的misfire对应的处理规则
CronTrigg
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

相关文章
相关标签/搜索