Quartz 學習

進階的小碼農發表於2018-08-19

任務排程:排程器、任務、觸發器

任務

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時,會執行兩次

Quartz 學習

/*
* 描述觸發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();
}
複製程式碼

相關文章