任務排程:排程器、任務、觸發器
任務
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();
}
複製程式碼