05、Quartz 实战 - 处理因执行job超时而错过触发的job

例子将执行以下操作:

实例化2个Job , 每个job 5秒执行一次,一直循环 直到Scheduler结束,但是job需要10秒执行完毕

StatefulDumbJob.java

package cn.zto.job;
import java.text.SimpleDateFormat;
import java.util.Date;
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
{
  public static final String NUM_EXECUTIONS = "NumExecutions";
  public static final String EXECUTION_DELAY = "ExecutionDelay";
  public void execute(JobExecutionContext context)
    throws JobExecutionException
  {
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
    System.err.println("---"+ dateFormat.format(new Date()) + " :执行 " +context.getJobDetail().getKey());
    JobDataMap map = context.getJobDetail().getJobDataMap();
    int executeCount = 0;
    if (map.containsKey("NumExecutions")) {
      executeCount = map.getInt("NumExecutions");
    }
    ++executeCount;
    map.put("NumExecutions", executeCount);
    long delay = 5000L;
    if (map.containsKey("ExecutionDelay")) {
      delay = map.getLong("ExecutionDelay");
    }
    try{
      Thread.sleep(delay);
    }
    catch (Exception ignore){
    }
    System.err.println("---" + dateFormat.format(new Date()) +" :"+ context.getJobDetail().getKey() + " 完成次数 (" + executeCount + ").");
  }
}

MisfireExample.java

package cn.zto.app;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.DateBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.zto.job.StatefulDumbJob;
public class MisfireExample
{
  public void run() throws Exception{
	System.out.println("------- 初始化 -------------------");
    SchedulerFactory sf = new StdSchedulerFactory();
    Scheduler sched = sf.getScheduler();
    System.out.println("------- 初始化完成 --------");
    System.out.println("------- 向Scheduler加入Job ----------------");
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
    Date startTime = DateBuilder.nextGivenSecondDate(null, 15);
    JobDetail job = JobBuilder.newJob(StatefulDumbJob.class)
    		.withIdentity("statefulJob1", "group1")
    		.usingJobData("ExecutionDelay", Long.valueOf(10000L))
    		.build();
    SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
    		.withIdentity("trigger1", "group1")
    		.startAt(startTime)
    		.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
    		.build();
    Date ft = sched.scheduleJob(job, trigger);
    System.out.println(job.getKey() + " 将在: " + dateFormat.format(ft) + " 是运行,重复: " + trigger.getRepeatCount() + " 次,每 " + trigger.getRepeatInterval() / 1000L + " 秒执行一次");
    job = JobBuilder.newJob(StatefulDumbJob.class)
    		.withIdentity("statefulJob2", "group1")
    		.usingJobData("ExecutionDelay", Long.valueOf(10000L))
    		.build();
    trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
    		.withIdentity("trigger2", "group1")
    		.startAt(startTime)
    		.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()
    				//设置失败指令:表示当job因为job执行时间过长 而 错过触发器时 job执行完后立即再次执行job
    				.withMisfireHandlingInstructionNowWithExistingCount())
    		.build();
    ft = sched.scheduleJob(job, trigger);
    System.out.println(job.getKey() + " 将在: " + dateFormat.format(ft) + " 是运行,重复: " + trigger.getRepeatCount() + " 次,每 " + trigger.getRepeatInterval() / 1000L + " 秒执行一次");
    System.out.println("------- 开始Scheduler ----------------");
    sched.start();
    System.out.println("------- Scheduler调用job结束 -----------------");
    System.out.println("------- 等待10分钟... --------------");
    try{
      Thread.sleep(600000L);
    }
    catch (Exception e){
    }
    System.out.println("------- 关闭Scheduler ---------------------");
    sched.shutdown(true);
    System.out.println("------- 关闭完成 -----------------");
    SchedulerMetaData metaData = sched.getMetaData();
    System.out.println("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
  }
  public static void main(String[] args) throws Exception
  {
    MisfireExample example = new MisfireExample();
    example.run();
  }
}

job1:没有设置触发失败指令 所以 job1会在job执行完成后 下一个触发器触发的时间执行job

job2:设置了触发失败指令所以当job2执行完成后 不管有没有到触发时间 都会执行job

最终的结果是 job2的执行次数大于job1的执行次数

为job加上@DisallowConcurrentExecution注解后job执行就会阻塞,在上一次job执行完毕后再去执行下一次job

如果没加上@DisallowConcurrentExecution注解只要到了触发器触发的时间,就会执行一次job,

因为上述的job有对数据进行操作如果上一个job没有执行结束,下一个job开始执行,就会对数据进行脏写:

这个是没加@DisallowConcurrentExecution注解的影响:

------- 初始化 -------------------
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
------- 初始化完成 --------
------- 向Scheduler加入Job ----------------
group1.statefulJob1 将在: 20140731185600秒 是运行,重复: -1 次,每 3 秒执行一次
group1.statefulJob2 将在: 20140731185600秒 是运行,重复: -1 次,每 3 秒执行一次
------- 开始Scheduler ----------------
------- Scheduler调用job结束 -----------------
------- 等待10分钟... --------------
---20140731185600秒 :执行 group1.statefulJob1
---20140731185600秒 :执行 group1.statefulJob2
---20140731185603秒 :执行 group1.statefulJob1
---20140731185603秒 :执行 group1.statefulJob2
---20140731185606秒 :执行 group1.statefulJob1
---20140731185606秒 :执行 group1.statefulJob2
---201407311856分09秒 :执行 group1.statefulJob1
---201407311856分09秒 :执行 group1.statefulJob2
---20140731185610秒 :group1.statefulJob1 完成次数 (1).
---20140731185610秒 :group1.statefulJob2 完成次数 (1).
---20140731185612秒 :执行 group1.statefulJob1
---20140731185612秒 :执行 group1.statefulJob2
---20140731185613秒 :group1.statefulJob2 完成次数 (1).
---20140731185613秒 :group1.statefulJob1 完成次数 (1).
---20140731185615秒 :执行 group1.statefulJob1
---20140731185615秒 :执行 group1.statefulJob2
---20140731185616秒 :group1.statefulJob2 完成次数 (1).
---20140731185616秒 :group1.statefulJob1 完成次数 (1).
---20140731185618秒 :执行 group1.statefulJob1
---20140731185618秒 :执行 group1.statefulJob2
---20140731185619秒 :group1.statefulJob2 完成次数 (1).
---20140731185619秒 :group1.statefulJob1 完成次数 (1).
---20140731185621秒 :执行 group1.statefulJob1
---20140731185621秒 :执行 group1.statefulJob2
---20140731185622秒 :group1.statefulJob2 完成次数 (2).
---20140731185622秒 :group1.statefulJob1 完成次数 (2).
---20140731185624秒 :执行 group1.statefulJob1
---20140731185624秒 :执行 group1.statefulJob2
---20140731185625秒 :group1.statefulJob1 完成次数 (2).
---20140731185625秒 :group1.statefulJob2 完成次数 (2).
---20140731185627秒 :执行 group1.statefulJob1
---20140731185627秒 :执行 group1.statefulJob2
---20140731185628秒 :group1.statefulJob1 完成次数 (2).
---20140731185628秒 :group1.statefulJob2 完成次数 (2).

加上注解后:

------- 初始化 -------------------
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
------- 初始化完成 --------
------- 向Scheduler加入Job ----------------
group1.statefulJob1 将在: 20140731173545秒 是运行,重复: -1 次,每 3 秒执行一次
group1.statefulJob2 将在: 20140731173545秒 是运行,重复: -1 次,每 3 秒执行一次
------- 开始Scheduler ----------------
------- Scheduler调用job结束 -----------------
------- 等待10分钟... --------------
---20140731173545秒 :执行 group1.statefulJob1
---20140731173545秒 :执行 group1.statefulJob2
---20140731173555秒 :group1.statefulJob1 完成次数 (1).
---20140731173555秒 :group1.statefulJob2 完成次数 (1).
---20140731173555秒 :执行 group1.statefulJob2
---20140731173555秒 :执行 group1.statefulJob1
---20140731173605秒 :group1.statefulJob2 完成次数 (2).
---20140731173605秒 :group1.statefulJob1 完成次数 (2).
---20140731173605秒 :执行 group1.statefulJob1
---20140731173605秒 :执行 group1.statefulJob2
---20140731173615秒 :group1.statefulJob1 完成次数 (3).
---20140731173615秒 :group1.statefulJob2 完成次数 (3).
---20140731173615秒 :执行 group1.statefulJob1
---20140731173615秒 :执行 group1.statefulJob2
---20140731173625秒 :group1.statefulJob2 完成次数 (4).
---20140731173625秒 :group1.statefulJob1 完成次数 (4).
---20140731173625秒 :执行 group1.statefulJob1
---20140731173625秒 :执行 group1.statefulJob2
---20140731173635秒 :group1.statefulJob1 完成次数 (5).
---20140731173635秒 :group1.statefulJob2 完成次数 (5).
---20140731173635秒 :执行 group1.statefulJob1
---20140731173635秒 :执行 group1.statefulJob2
---20140731173645秒 :group1.statefulJob1 完成次数 (6).
---20140731173645秒 :group1.statefulJob2 完成次数 (6).
---20140731173645秒 :执行 group1.statefulJob2
---20140731173645秒 :执行 group1.statefulJob1
---20140731173655秒 :group1.statefulJob2 完成次数 (7).
---20140731173655秒 :group1.statefulJob1 完成次数 (7).
---20140731173655秒 :执行 group1.statefulJob1
---20140731173655秒 :执行 group1.statefulJob2
---20140731173705秒 :group1.statefulJob1 完成次数 (8).
---20140731173705秒 :执行 group1.statefulJob1
---20140731173705秒 :group1.statefulJob2 完成次数 (8).
---20140731173705秒 :执行 group1.statefulJob2
---20140731173715秒 :group1.statefulJob1 完成次数 (9).
---20140731173715秒 :group1.statefulJob2 完成次数 (9).