Quartz 学习

405 阅读6分钟

任务调度:调度器、任务、触发器

任务

Job

  • 拥有JobDataMap属性,保存信息
  • 每次执行execute方法前都会创建一个新的Job 实例,执行完后会被丢弃回收
@PersistJobDataAfterExecution 持久化JobDataMap
@DisallowConcurrentExecution   不允许并发执行
RequestsRecovery  系统崩溃后,可恢复执行
Durability 非持久的话,当没有活跃Trigger与其绑定时,job会被自动删除
 /* 
 * 实现此接口定义需要执行的任务
 * 实现类必须包含一个公共的无参构造器
 * JobDataMap保存了Job 运行时的信息         
 */
public interface Job {
    // JobExecutionContext 提供调度上下文信息
    void execute(JobExecutionContext context)throws JobExecutionException;
}

JobDetail

/*
* 用来描述Job实例的静态信息,如Job名称、描述、关联监听器等信息
* 通过 Job名(name)和组名(group)定位唯一的Job实例
*/
public interface JobDetail {
    public JobKey getKey();
    public String getDescription();
    public Class<? extends Job> getJobClass();
    public JobDataMap getJobDataMap();
}

JobBuilder

/*
* 用来创建JobDetail
*/
public class JobBuilder {

    public JobDetail build() {
    
        JobDetailImpl job = new JobDetailImpl();
        job.setJobClass(jobClass);
        job.setDescription(description);
        if(key == null)
            key = new JobKey(Key.createUniqueName(null), null);
        job.setKey(key); 
        job.setDurability(durability);
        job.setRequestsRecovery(shouldRecover);
        
        if(!jobDataMap.isEmpty())
            job.setJobDataMap(jobDataMap);
        
        return job;
    }
}

JobDetail job = newJob(MyJob.class)
    .withIdentity("myJob")
    .build();

触发器

Trigger

  • 拥有JobDataMap属性,保存信息,可用于传参数给Job
  • 不同Trigger之间相互独立,两个不同Trigger同时触发一个Job时,会执行两次

/*
* 描述触发Job执行的时间规则
* SimpleTriger、CronTrigger
*/
public interface Trigger {
    public TriggerKey getKey();
    public JobKey getJobKey();
    public JobDataMap getJobDataMap();
}

SimpleTrigger

  • 以固定时间间隔重复执行
  • endTime到了指定后,即使设置的 repeatCount大于零,trigger也会终止。
startTime ,endTime, repeat count, repeat interval

public interface SimpleTrigger extends Trigger {
    public int getRepeatCount();
    public long getRepeatInterval();
    public int getTimesTriggered();
}

Trigger trigger = TriggerBuilder.newTrigger()
	.withIdentity("trigger1", "group1")
	//开始触发时间,现在设置为程序启动后3秒
	.startAt(DateTime.now().plusSeconds(3).toDate())
	//设置重复时间间隔interval、重复次数repeatCount
	.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))
	.build();
	

CronTrigger

public interface CronTrigger extends Trigger {
    public String getCronExpression();
    public String getExpressionSummary();
    TriggerBuilder<CronTrigger> getTriggerBuilder();
}

Trigger trigger = TriggerBuilder.newTrigger()
	.withIdentity("trigger1", "group1")
	// cron 表达式
	.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(15,0))
	//.withSchedule(cronSchedule("0/20 * * * * ?"))
	.build();

TriggerBuilder

public class TriggerBuilder<T extends Trigger> {

    private TriggerKey key;
    private String description;
    private Date startTime = new Date();
    private Date endTime;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String calendarName;
    private JobKey jobKey;
    private JobDataMap jobDataMap = new JobDataMap();
    
    public static TriggerBuilder<Trigger> newTrigger() {
        return new TriggerBuilder<Trigger>();
    }
    
    public TriggerBuilder<T> withIdentity(String name, String group) {
        key = new TriggerKey(name, group);
        return this;
    }
    
    public TriggerBuilder<T> startAt(Date triggerStartTime) {
        this.startTime = triggerStartTime;
        return this;
    }
    
    public TriggerBuilder<T> endAt(Date triggerEndTime) {
        this.endTime = triggerEndTime;
        return this;
    }
    
    public <SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) {
        this.scheduleBuilder = schedBuilder;
        return (TriggerBuilder<SBT>) this;
    }
    
    public TriggerBuilder<T> forJob(JobKey keyOfJobToFire) {
        this.jobKey = keyOfJobToFire;
        return this;
    }
    
     public TriggerBuilder<T> usingJobData(JobDataMap newJobDataMap) {
        // add any existing data to this new map
        for(String dataKey: jobDataMap.keySet()) {
            newJobDataMap.put(dataKey, jobDataMap.get(dataKey));
        }
        jobDataMap = newJobDataMap; // set new map as the map to use
        return this;
    }
    newTrigger()
    .withIdentity("trigger3", "group1")
    .startAt(myTimeToStartFiring)  // if a start time is not given (if this line were omitted), "now" is implied
    .withSchedule(simpleSchedule()
        .withIntervalInSeconds(10)
        .withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
    // 绑定到具体jobDetail
    .forJob(myJob) // identify job with handle to its JobDetail itself  .endAt(dateOf(22, 0, 0))             
    .build();

调度器

Scheduler

  • 代表一个Quartz的独立运行容器
  • 将Trigger绑定到某一JobDetail,当Trigger被触发时,对应的Job被执行
  • 一个Scheduler可以拥有多个Trigger和多个Job
  • 一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job
  • scheduler启动后仍可注册任务和trigger
public interface Scheduler {
    String getSchedulerName() throws SchedulerException;
    String getSchedulerInstanceId() throws SchedulerException;
    //获取Scheduler实例的上下文信息
    SchedulerContext getContext() throws SchedulerException;
    void start() throws SchedulerException;
    void standby() throws SchedulerException;
    void shutdown() throws SchedulerException;
    List<JobExecutionContext> getCurrentlyExecutingJobs() 
        throws SchedulerException;
        
    //将jobDetail和trigger注册到Scheduler
    //用trigger对jobDetail进行调度
    Date scheduleJob(JobDetail jobDetail, Trigger trigger)
        throws SchedulerException;
    // 先注册jobDetail,后注册指定了关联jobDetail的Trigger
     void addJob(JobDetail jobDetail, boolean replace)
        throws SchedulerException;
    //trigger必须绑定jobDetail
    Date scheduleJob(Trigger trigger) 
        throws SchedulerException;
        
    boolean unscheduleJob(TriggerKey triggerKey)
        throws SchedulerException;
    Date rescheduleJob(TriggerKey triggerKey, Trigger newTrigger) 
        throws SchedulerException;
     boolean deleteJob(JobKey jobKey)
        throws SchedulerException;
     void triggerJob(JobKey jobKey)
        throws SchedulerException;
    void pauseJob(JobKey jobKey)
        throws SchedulerException;
    void pauseTrigger(TriggerKey triggerKey)
        throws SchedulerException;
    void resumeJob(JobKey jobKey)
        throws SchedulerException;
    void resumeTrigger(TriggerKey triggerKey)
        throws SchedulerException;
     List<? extends Trigger> getTriggersOfJob(JobKey jobKey)
        throws SchedulerException;
    JobDetail getJobDetail(JobKey jobKey)
        throws SchedulerException;
    Trigger getTrigger(TriggerKey triggerKey)
        throws SchedulerException;
        
}   
public class StdScheduler implements Scheduler {
    
}    
    

QuartzScheduler

Scheduler的间接实现,由StdScheduler代理或适配

public class QuartzScheduler implements RemotableQuartzScheduler {

    public Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
        // 检查调度器是否开启,如果关闭则throw异常到上层 validateState(); ...... 
        // 获取trigger首次触发job的时间,以此时间为起点,每隔一段指定的时间触发job
        Date ft = trig.computeFirstFireTime(cal); 
        if (ft == null) { 
            throw new SchedulerException( "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire."); 
        }
        // 把job和trigger注册进调度器的jobStore resources.getJobStore().storeJobAndTrigger(jobDetail, trig); 
        // 通知job监听者 
        notifySchedulerListenersJobAdded(jobDetail);
        // 通知调度器线程 notifySchedulerThread(trigger.getNextFireTime().getTime()); 
        // 通知trigger监听者 
        notifySchedulerListenersSchduled(trigger); 
        return ft; 
        
    }

}
public Scheduler getScheduler() throws SchedulerException { 
// 读取quartz配置文件,未指定则顺序遍历各个path下的quartz.properties文件 
// 解析出quartz配置内容和环境变量,存入PropertiesParser对象 
// PropertiesParser组合了Properties(继承Hashtable),定义了一系列对Properties的操作方法,比如getPropertyGroup()批量获取相同前缀的配置。配置内容和环境变量存放在Properties成员变量中
    if (cfg == null) { 
        initialize();
    } 
// 获取调度器池,采用了单例模式
// 其实,调度器池的核心变量就是一个hashmap,每个元素key是scheduler名,value是scheduler实例 
// getInstance()用synchronized防止并发创建 
    SchedulerRepository schedRep = SchedulerRepository.getInstance();

// 从调度器池中取出当前配置所用的调度器 
    Scheduler sched = schedRep.lookup(getSchedulerName()); 
。。。。。。
// 如果调度器池中没有当前配置的调度器,则实例化一个调度器,主要动作包括: 
// 1)初始化threadPool(线程池):开发者可以通过org.quartz.threadPool.class配置指定使用哪个线程池类,比如SimpleThreadPool。先class load线程池类,接着动态生成线程池实例bean,然后通过反射,使用setXXX()方法将以org.quartz.threadPool开头的配置内容赋值给bean成员变量;
// 2)初始化jobStore(任务存储方式):开发者可以通过org.quartz.jobStore.class配置指定使用哪个任务存储类,比如RAMJobStore。先class load任务存储类,接着动态生成实例bean,然后通过反射,使用setXXX()方法将以org.quartz.jobStore开头的配置内容赋值给bean成员变量; 
// 3)初始化dataSource(数据源):开发者可以通过org.quartz.dataSource配置指定数据源详情,比如哪个数据库、账号、密码等。jobStore要指定为JDBCJobStore,dataSource才会有效;
// 4)初始化其他配置:包括SchedulerPlugins、JobListeners、TriggerListeners等; 
// 5)初始化threadExecutor(线程执行器):默认为DefaultThreadExecutor; 
// 6)创建工作线程:根据配置创建N个工作thread,执行start()启动thread,并将N个thread顺序add进threadPool实例的空闲线程列表availWorkers中; 
// 7)创建调度器线程:创建QuartzSchedulerThread实例,并通过threadExecutor.execute(实例)启动调度器线程;
// 8)创建调度器:创建StdScheduler实例,将上面所有配置和引用组合进实例中,并将实例存入调度器池中 
    sched = instantiate(); 
    return sched; 
    
}

SchedulerFactory

用于创建Scheduler实例

public interface SchedulerFactory {
    Scheduler getScheduler() throws SchedulerException;
    Scheduler getScheduler(String schedName) throws SchedulerException;
    Collection<Scheduler> getAllSchedulers() throws SchedulerException;
}

JobDataMap

jobDataMap 存储状态实例

  • 必须加注解 @PersistJobDataAfterExecution,才能存储相关信息供以后触发使用
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class ColorJob implements Job {

    public static final String FAVORITE_COLOR = "favorite color";
    public static final String EXECUTION_COUNT = "count";

    public ColorJob() {
    }

    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        
        JobKey jobKey = context.getJobDetail().getKey();
        JobDataMap data = context.getJobDetail().getJobDataMap();
        String favoriteColor = data.getString(FAVORITE_COLOR);
        int count = data.getInt(EXECUTION_COUNT);
        count++;
        data.put(EXECUTION_COUNT, count);
    }

}

JobDetail job2 = newJob(ColorJob.class)
            .withIdentity("job2", "group1").build();

SimpleTrigger trigger2 = newTrigger()
        .withIdentity("trigger2", "group1")
        .startAt(startTime)
        .withSchedule(simpleSchedule().withIntervalInSeconds(3).withRepeatCount(4))
        .build();

// pass initialization parameters into the job
// this job has a different favorite color!
job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);

Listener

TriggerListener

public interface TriggerListener {

    public String getName();

    public void triggerFired(Trigger trigger, JobExecutionContext context);

    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);

    public void triggerMisfired(Trigger trigger);

    public void triggerComplete(Trigger trigger, JobExecutionContext context,
            int triggerInstructionCode);
}

public abstract class TriggerListenerSupport implements TriggerListener {
}

JobListener

public interface JobListener {

    public String getName();

    public void jobToBeExecuted(JobExecutionContext context);

    public void jobExecutionVetoed(JobExecutionContext context);

    public void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException);

}

public abstract class JobListenerSupport implements JobListener {
}

JobListener listener = new Job1Listener();
Matcher<JobKey> matcher = KeyMatcher.keyEquals(job.getKey());
sched.getListenerManager().addJobListener(listener, matcher);

SchedulerListener

public interface SchedulerListener {

    public void jobScheduled(Trigger trigger);

    public void jobUnscheduled(String triggerName, String triggerGroup);

    public void triggerFinalized(Trigger trigger);

    public void triggersPaused(String triggerName, String triggerGroup);

    public void triggersResumed(String triggerName, String triggerGroup);

    public void jobsPaused(String jobName, String jobGroup);

    public void jobsResumed(String jobName, String jobGroup);

    public void schedulerError(String msg, SchedulerException cause);

    public void schedulerStarted();

    public void schedulerInStandbyMode();

    public void schedulerShutdown();

    public void schedulingDataCleared();
}