Quartz是一個開放源碼項目,專注於任務調度器,提供了極為廣泛的特性如持久化任務,集群和分布式任務等。 Quartz核心是調度器,還采用多線程管理。
1.持久化任務:當應用程序停止運行時,所有調度信息不被丟失,當你重新啟動時,調度信息還存在,這就是持久化任務。
2.集群和分布式處理:當在集群環境下,當有配置Quartz的多個客戶端時(節點),采用Quartz的集群和分布式處理時,我們要了解幾點好處 1) 一個節點無法完成的任務,會被集群中擁有相同的任務的節點取代執行。2) Quartz調度是通過觸發器的類別來識別不同的任務,在不同的節點定義相同的觸發器的類別,這樣在集群下能穩定的運行,一個節點無法完成的任務,會被集群中擁有相同的任務的節點取代執行。3)分布式 體現在 當相同的任務定時在一個時間點,在那個時間點,不會被兩個節點同時執行。
Quartz的 Task(11 張表)實例化采用數據庫存儲,基於數據庫引擎及 High-Available 的策略(集群的一種策略)自動協調每個節點的 Quartz。
delete from qrtz_fired_triggers;
delete from qrtz_simple_triggers;
delete from qrtz_simprop_triggers;
delete from qrtz_cron_triggers;
delete from qrtz_blob_triggers;
delete from qrtz_triggers;
delete from qrtz_job_details;
delete from qrtz_calendars;
delete from qrtz_paused_trigger_grps;
delete from qrtz_locks;
delete from qrtz_scheduler_state;
CREATE TABLE qrtz_job_details
(
SCHED_NAME VARCHAR2(120) NOT NULL,
JOB_NAME VARCHAR2(200) NOT NULL,
JOB_GROUP VARCHAR2(200) NOT NULL,
DESCRIPTION VARCHAR2(250) NULL,
JOB_CLASS_NAME VARCHAR2(250) NOT NULL,
IS_DURABLE VARCHAR2(1) NOT NULL,
IS_NONCONCURRENT VARCHAR2(1) NOT NULL,
IS_UPDATE_DATA VARCHAR2(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,
JOB_DATA BLOB NULL,
CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
JOB_NAME VARCHAR2(200) NOT NULL,
JOB_GROUP VARCHAR2(200) NOT NULL,
DESCRIPTION VARCHAR2(250) NULL,
NEXT_FIRE_TIME NUMBER(13) NULL,
PREV_FIRE_TIME NUMBER(13) NULL,
PRIORITY NUMBER(13) NULL,
TRIGGER_STATE VARCHAR2(16) NOT NULL,
TRIGGER_TYPE VARCHAR2(8) NOT NULL,
START_TIME NUMBER(13) NOT NULL,
END_TIME NUMBER(13) NULL,
CALENDAR_NAME VARCHAR2(200) NULL,
MISFIRE_INSTR NUMBER(2) NULL,
JOB_DATA BLOB NULL,
CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_simple_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
REPEAT_COUNT NUMBER(7) NOT NULL,
REPEAT_INTERVAL NUMBER(12) NOT NULL,
TIMES_TRIGGERED NUMBER(10) NOT NULL,
CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_cron_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
CRON_EXPRESSION VARCHAR2(120) NOT NULL,
TIME_ZONE_ID VARCHAR2(80),
CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_simprop_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
STR_PROP_1 VARCHAR2(512) NULL,
STR_PROP_2 VARCHAR2(512) NULL,
STR_PROP_3 VARCHAR2(512) NULL,
INT_PROP_1 NUMBER(10) NULL,
INT_PROP_2 NUMBER(10) NULL,
LONG_PROP_1 NUMBER(13) NULL,
LONG_PROP_2 NUMBER(13) NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR2(1) NULL,
BOOL_PROP_2 VARCHAR2(1) NULL,
CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_blob_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
BLOB_DATA BLOB NULL,
CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_calendars
(
SCHED_NAME VARCHAR2(120) NOT NULL,
CALENDAR_NAME VARCHAR2(200) NOT NULL,
CALENDAR BLOB NOT NULL,
CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE qrtz_paused_trigger_grps
(
SCHED_NAME VARCHAR2(120) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_fired_triggers
(
SCHED_NAME VARCHAR2(120) NOT NULL,
ENTRY_ID VARCHAR2(95) NOT NULL,
TRIGGER_NAME VARCHAR2(200) NOT NULL,
TRIGGER_GROUP VARCHAR2(200) NOT NULL,
INSTANCE_NAME VARCHAR2(200) NOT NULL,
FIRED_TIME NUMBER(13) NOT NULL,
SCHED_TIME NUMBER(13) NOT NULL,
PRIORITY NUMBER(13) NOT NULL,
STATE VARCHAR2(16) NOT NULL,
JOB_NAME VARCHAR2(200) NULL,
JOB_GROUP VARCHAR2(200) NULL,
IS_NONCONCURRENT VARCHAR2(1) NULL,
REQUESTS_RECOVERY VARCHAR2(1) NULL,
CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE qrtz_scheduler_state
(
SCHED_NAME VARCHAR2(120) NOT NULL,
INSTANCE_NAME VARCHAR2(200) NOT NULL,
LAST_CHECKIN_TIME NUMBER(13) NOT NULL,
CHECKIN_INTERVAL NUMBER(13) NOT NULL,
CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE qrtz_locks
(
SCHED_NAME VARCHAR2(120) NOT NULL,
LOCK_NAME VARCHAR2(40) NOT NULL,
CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);
create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);
create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);
create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);
create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);
create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);
我在新建一個張表,為了方便頁面能對每個任務進行管理,能對具體某個任務設置開始時間、結束時間、執行的方法、刪除等, 如下面圖所示:


在這邊可以管理開始時間和結束時間和cronExpression值,方便管理對應表的設計:


表都設計好了,整理Quartz集成springMVC的具體的實現。
對Spring的@component 的說明:@component (把普通pojo實例化到spring容器中,相當於配置文件中的<bean id="" class=""/>)
1.舉例兩個任務具體實現功能,列如quartzJobA和quartzJobB任務要做什么,新建了兩個類和方法。
import org.springframework.stereotype.Component;
@Component("quartzJobA")
public class Data2ServiceImpl {
public void run() {
System.out.println("=============Data2ServiceImpl=========");
}
}
@Component("quartzJobB")
public class DataServiceImpl {
public void test() {
System.out.println("=============DataServiceImpl=========");
}
}
2.Quartz 調度任務所需的配置文件 quartz-job.properties
#Main Scheduler Settings org.quartz.scheduler.instanceName=quartzScheduler org.quartz.scheduler.instanceId=AUTO org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer=true org.quartz.scheduler.skipUpdateCheck=true org.quartz.scheduler.batchTriggerAcquisitionMaxCount=100 org.quartz.threadPool.threadCount=10 #Configure JDBC-JobStoreTX org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.jobStore.isClustered=true org.quartz.jobStore.acquireTriggersWithinLock=true org.quartz.jobStore.clusterCheckinInterval = 30000 #Configure DataSources org.quartz.dataSource.myDS.driver=com.alibaba.druid.proxy.DruidDriver org.quartz.dataSource.myDS.URL=jdbc:wrap-jdbc:filters=default:name=dragoon:jdbc:oracle:thin:@127.0.0.1:1521:test org.quartz.dataSource.myDS.user= org.quartz.dataSource.myDS.password= org.quartz.dataSource.myDS.maxConnections=5 org.quartz.dataSource.myDS.validationQuery=select 1 from dual
org.quartz.scheduler.instanceName 屬性可為任何值,用在 JDBC JobStore 中來唯一標識實例,但是所有集群節點中必須相同
org.quartz.jobStore.class屬性為 JobStoreTX,將任務持久化到數據中。因為集群中節點依賴於數據庫來傳播 Scheduler 實例的狀態,你只能在使用 JDBC JobStore 時應用 Quartz 集群。這意味着你必須使用 JobStoreTX 或是 JobStoreCMT 作為 Job 存儲;你不能在集群中使用 RAMJobStore
3.實現任務的創建和管理
@Component("schedulerHelper")
public class SchedulerHelper
{
private static final String CONFIG_FILE="quartz-job.properties";
private static final String IDENTITY_JOB_PREFIX="job_";
private static final String IDENTITY_TRIGGER_PREFIX="trigger_";
@Autowired
private JobService jobService;//jobService 這個服務是實現管理任務的頁面的服務實現
private Scheduler scheduler;
@Autowired
private StartJobSchedulerListener startJobSchedulerListener;//實現自己的Scheduler監聽器,程序啟動時,任務沒創建時就創建
/**
* tomcat一啟動時,類實例化時就執行
*/
public void init()
{
try{
// 創建一個定時器工廠
StdSchedulerFactory sf = new StdSchedulerFactory();
//初始化quartz-job.properties配置文件
sf.initialize(Thread.currentThread().getContextClassLoader().getResource(CONFIG_FILE).getFile());
scheduler = sf.getScheduler();
//把jobService放到scheduler上下文,job執行是可以獲取並訪問。
scheduler.getContext().put(SCHEDULER_KEY_JOBSERVICE,jobService);
startJobSchedulerListener.setSchedulerHelper(this);
//設置自己的監聽器
scheduler.getListenerManager().addSchedulerListener(startJobSchedulerListener);
// 啟動定時器
scheduler.start();
logger.info("====================job scheduler start");
}catch(SchedulerException e){
logger.error("error",e);
}
}
/**
* 根據jobentity創建並開始任務
*/
public boolean createAndStartJob(JobEntity job)
{
JobDetail jobDetail=generateJobDetail(job);
Trigger trigger=generateTriggerBuilder(job).build();
try {
scheduler.scheduleJob(jobDetail, trigger);
return true;
} catch (SchedulerException e) {
logger.error("scheduler.scheduleJob",e);
return false;
}
}
/**
* 清除
*/
public void clearAllScheduler()
{
try {
scheduler.clear();
} catch (SchedulerException e) {
logger.error("clearAllScheduler",e);
}
}
/**
* 根據jobId和類型刪除
*/
public boolean removeJob(Long jobId,String jobType)
{
try {
scheduler.deleteJob(getJobKey(jobId,jobType));
return true;
} catch (SchedulerException e) {
logger.error("removeJob",e);
return false;
}
}
/**
* 暫停任務
*/
public boolean pauseJob(Long jobId,String jobType)
{
try {
scheduler.pauseJob(getJobKey(jobId,jobType));
return true;
} catch (SchedulerException e) {
logger.error("resumeJob",e);
return false;
}
}
/**
* 馬上只執行一次任務
*/
public boolean executeOneceJob(Long jobId,String jobType)
{
try {
Calendar end=Calendar.getInstance();
TriggerBuilder<SimpleTrigger> simpleTriggerBuilder=TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(jobId,jobType))
.forJob(getJobKey(jobId,jobType))
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2));
end.add(Calendar.SECOND, 2);
simpleTriggerBuilder.startAt(end.getTime());
end.add(Calendar.SECOND, 5);
simpleTriggerBuilder.endAt(end.getTime());
JobEntity job=jobService.getJobById(jobId);
JobDataMap jobDataMap=new JobDataMap();
jobDataMap.put("jobEntity", job);
simpleTriggerBuilder.usingJobData(jobDataMap);
Trigger trigger=simpleTriggerBuilder.build();
scheduler.scheduleJob(trigger);
return true;
} catch (SchedulerException e) {
logger.error("executeOneceJob",e);
return false;
}
}
/**
* 啟動一些scheduler里沒有的active的jobDetail
*/
public void createActiveJobFromDB() throws SchedulerException
{
List<JobEntity> jobs=jobService.getActiveJob();
for(JobEntity job:jobs)
{
if(scheduler.getJobDetail(getJobKey(job))==null)
createAndStartJob(job);
}
}
/**
* 獲得任務的jobKey
*/
public static JobKey getJobKey(Long jobId,String jobType)
{
return new JobKey(IDENTITY_JOB_PREFIX+jobId,IDENTITY_JOB_PREFIX+jobType);
}
/**
* 獲得任務的jobKey
*/
public static JobKey getJobKey(JobEntity job)
{
return new JobKey(IDENTITY_JOB_PREFIX+job.getJobId(),IDENTITY_JOB_PREFIX+job.getJobType());
}
/**
* 獲得trigger的triggerkey
*/
public static TriggerKey getTriggerKey(JobEntity job)
{
return new TriggerKey(IDENTITY_TRIGGER_PREFIX+job.getJobId()+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+job.getJobType());
}
/**
* 獲得trigger的triggerkey
*/
public static TriggerKey getTriggerKey(Long jobId,String jobType)
{
return new TriggerKey(IDENTITY_TRIGGER_PREFIX+jobId+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+jobType);
}
public static JobDetail generateJobDetail(JobEntity job)
{
JobDataMap jobDataMap=new JobDataMap();
jobDataMap.put("jobEntity", job);
Class<? extends Job> clazz=null;
clazz=BeanJob.class;
return JobBuilder.newJob(clazz)
.withIdentity(getJobKey(job))
.usingJobData(jobDataMap)
.requestRecovery(true).storeDurably(true)
.build();
}
/**
* 根據jobEntity獲得trigger
*/
public static TriggerBuilder<CronTrigger> generateTriggerBuilder(JobEntity job)
{
TriggerBuilder<CronTrigger> triggerBuilder= TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(job))
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpr())
.withMisfireHandlingInstructionDoNothing());
if(job.getSyncBeginTime()!=null)
triggerBuilder.startAt(job.getSyncBeginTime());
else
triggerBuilder.startNow();
if(job.getSyncEndTime()!=null)
triggerBuilder.endAt(job.getSyncEndTime());
return triggerBuilder;
}
public static JobService getJobService(JobExecutionContext context)
{
try {
return (JobService) context.getScheduler().getContext().get(SchedulerHelper.SCHEDULER_KEY_JOBSERVICE);
} catch (SchedulerException e) {
logger.error("SchedulerHelper.getJobService",e);
return null;
}
}
@Component("schedulerHelper")
public class SchedulerHelper
{
private static final String CONFIG_FILE="quartz-job.properties";
private static final String IDENTITY_JOB_PREFIX="job_";
private static final String IDENTITY_TRIGGER_PREFIX="trigger_";
@Autowired
private JobService jobService;//jobService 這個服務是實現管理任務的頁面的服務實現
private Scheduler scheduler;
@Autowired
private StartJobSchedulerListener startJobSchedulerListener;//實現自己的Scheduler監聽器,程序啟動時,任務沒創建時就創建
/**
* tomcat一啟動時,類實例化時就執行
*/
public void init()
{
try{
// 創建一個定時器工廠
StdSchedulerFactory sf = new StdSchedulerFactory();
//初始化quartz-job.properties配置文件
sf.initialize(Thread.currentThread().getContextClassLoader().getResource(CONFIG_FILE).getFile());
scheduler = sf.getScheduler();
//把jobService放到scheduler上下文,job執行是可以獲取並訪問。
scheduler.getContext().put(SCHEDULER_KEY_JOBSERVICE,jobService);
startJobSchedulerListener.setSchedulerHelper(this);
//設置自己的監聽器
scheduler.getListenerManager().addSchedulerListener(startJobSchedulerListener);
// 啟動定時器
scheduler.start();
logger.info("====================job scheduler start");
}catch(SchedulerException e){
logger.error("error",e);
}
}
/**
* 根據jobentity創建並開始任務
*/
public boolean createAndStartJob(JobEntity job)
{
JobDetail jobDetail=generateJobDetail(job);
Trigger trigger=generateTriggerBuilder(job).build();
try {
scheduler.scheduleJob(jobDetail, trigger);
return true;
} catch (SchedulerException e) {
logger.error("scheduler.scheduleJob",e);
return false;
}
}
/**
* 清除
*/
public void clearAllScheduler()
{
try {
scheduler.clear();
} catch (SchedulerException e) {
logger.error("clearAllScheduler",e);
}
}
/**
* 根據jobId和類型刪除
*/
public boolean removeJob(Long jobId,String jobType)
{
try {
scheduler.deleteJob(getJobKey(jobId,jobType));
return true;
} catch (SchedulerException e) {
logger.error("removeJob",e);
return false;
}
}
/**
* 暫停任務
*/
public boolean pauseJob(Long jobId,String jobType)
{
try {
scheduler.pauseJob(getJobKey(jobId,jobType));
return true;
} catch (SchedulerException e) {
logger.error("resumeJob",e);
return false;
}
}
/**
* 馬上只執行一次任務
*/
public boolean executeOneceJob(Long jobId,String jobType)
{
try {
Calendar end=Calendar.getInstance();
TriggerBuilder<SimpleTrigger> simpleTriggerBuilder=TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(jobId,jobType))
.forJob(getJobKey(jobId,jobType))
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2));
end.add(Calendar.SECOND, 2);
simpleTriggerBuilder.startAt(end.getTime());
end.add(Calendar.SECOND, 5);
simpleTriggerBuilder.endAt(end.getTime());
JobEntity job=jobService.getJobById(jobId);
JobDataMap jobDataMap=new JobDataMap();
jobDataMap.put("jobEntity", job);
simpleTriggerBuilder.usingJobData(jobDataMap);
Trigger trigger=simpleTriggerBuilder.build();
scheduler.scheduleJob(trigger);
return true;
} catch (SchedulerException e) {
logger.error("executeOneceJob",e);
return false;
}
}
/**
* 啟動一些scheduler里沒有的active的jobDetail
*/
public void createActiveJobFromDB() throws SchedulerException
{
List<JobEntity> jobs=jobService.getActiveJob();
for(JobEntity job:jobs)
{
if(scheduler.getJobDetail(getJobKey(job))==null)
createAndStartJob(job);
}
}
/**
* 獲得任務的jobKey
*/
public static JobKey getJobKey(Long jobId,String jobType)
{
return new JobKey(IDENTITY_JOB_PREFIX+jobId,IDENTITY_JOB_PREFIX+jobType);
}
/**
* 獲得任務的jobKey
*/
public static JobKey getJobKey(JobEntity job)
{
return new JobKey(IDENTITY_JOB_PREFIX+job.getJobId(),IDENTITY_JOB_PREFIX+job.getJobType());
}
/**
* 獲得trigger的triggerkey
*/
public static TriggerKey getTriggerKey(JobEntity job)
{
return new TriggerKey(IDENTITY_TRIGGER_PREFIX+job.getJobId()+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+job.getJobType());
}
/**
* 獲得trigger的triggerkey
*/
public static TriggerKey getTriggerKey(Long jobId,String jobType)
{
return new TriggerKey(IDENTITY_TRIGGER_PREFIX+jobId+"_"+System.currentTimeMillis(), IDENTITY_TRIGGER_PREFIX+jobType);
}
public static JobDetail generateJobDetail(JobEntity job)
{
JobDataMap jobDataMap=new JobDataMap();
jobDataMap.put("jobEntity", job);
Class<? extends Job> clazz=null;
clazz=BeanJob.class;
return JobBuilder.newJob(clazz)
.withIdentity(getJobKey(job))
.usingJobData(jobDataMap)
.requestRecovery(true).storeDurably(true)
.build();
}
/**
* 根據jobEntity獲得trigger
*/
public static TriggerBuilder<CronTrigger> generateTriggerBuilder(JobEntity job)
{
TriggerBuilder<CronTrigger> triggerBuilder= TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(job))
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpr())
.withMisfireHandlingInstructionDoNothing());
if(job.getSyncBeginTime()!=null)
triggerBuilder.startAt(job.getSyncBeginTime());
else
triggerBuilder.startNow();
if(job.getSyncEndTime()!=null)
triggerBuilder.endAt(job.getSyncEndTime());
return triggerBuilder;
}
public static JobService getJobService(JobExecutionContext context)
{
try {
return (JobService) context.getScheduler().getContext().get(SchedulerHelper.SCHEDULER_KEY_JOBSERVICE);
} catch (SchedulerException e) {
logger.error("SchedulerHelper.getJobService",e);
return null;
}
}
4.實現自己的Scheduler監聽器,程序啟動時,創建scheduler里沒有的active的jobDetail
@Component(value="startJobSchedulerListener")
public class StartJobSchedulerListener extends SchedulerListenerSupport
{
private SchedulerHelper schedulerHelper;
@Override
public void schedulerStarted()
{
try {
schedulerHelper.createActiveJobFromDB();
} catch (SchedulerException e) {
logger.error("createActiveJobFromDB",e);
}
}
public SchedulerHelper getSchedulerHelper() {
return schedulerHelper;
}
public void setSchedulerHelper(SchedulerHelper schedulerHelper) {
this.schedulerHelper = schedulerHelper;
}
}
5.實現的是一個job實例對應一個線程並實現頁面配置對應的哪個類和方法
public abstract class AbstractEdiJob implements Job
{
protected JobEntity jobEntity;
protected static final Logger logger=LoggerFactory.getLogger(AbstractEdiJob.class);
private Long beginTime;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobService jobService=SchedulerHelper.getJobService(context);
preExcute(jobService,context);
exeucuteInternal(context);
postExcute(jobService,context);
}
abstract public void exeucuteInternal(JobExecutionContext context);
public void preExcute(JobService jobService,JobExecutionContext context)
{
beginTime=System.currentTimeMillis();
}
public void postExcute(JobService jobService,JobExecutionContext context)
{
//獲得最新的jobEntiry
jobEntity=jobService.getJobById(jobEntity.getJobId());
if(jobEntity==null)
{
logger.warn(jobEntity.getJobId()+"job不能存在");
return;
}
if(context.getFireTime()!=null)
jobEntity.setRuntimeLast(context.getFireTime());
if(context.getNextFireTime()!=null)
jobEntity.setRuntimeNext(context.getNextFireTime());
/* else
jobEntity.setJobStatus();*/
Long times=jobEntity.getRunTimes();
jobEntity.setRunTimes((times==null?0l:times)+1);
Long duration=jobEntity.getRunDuration();
jobEntity.setRunDuration((duration==null?0l:duration)+(System.currentTimeMillis()-beginTime));
jobService.updateJob(jobEntity);
//jobEntity這里的改變不能改變JobDetail里的JobEntity,因為生產的job是JobDetail的JobEntity的復制
}
public void setJobEntity(JobEntity jobEntity) {
this.jobEntity = jobEntity;
}
}
/**
*執行具體類中的方法
**/
public class BeanJob extends AbstractEdiJob
{
private static Logger logger=LoggerFactory.getLogger(BeanJob.class);
@Override
public void exeucuteInternal(JobExecutionContext context)
{
Object obj=SpringContextUtil.getBean(jobEntity.getJobObject());
try {
Method method=obj.getClass().getMethod(jobEntity.getJobMethod());
method.invoke(obj);
} catch (SecurityException e) {
logger.error("error",e);
} catch (NoSuchMethodException e) {
logger.error("error",e);
} catch (IllegalArgumentException e) {
logger.error("error",e);
} catch (IllegalAccessException e) {
logger.error("error",e);
} catch (InvocationTargetException e) {
logger.error("error",e);
}
}
}
6.新增一個任務時,數據庫就保存對應的觸發器,變成持久化任務,如圖所示:

1.用StdSchedulerFactory來獲取Scheduler的實例,scheduler有啟動(start)、中止(stop)和暫停(pause)方法。
2.JobDataMap實例,JobDataMap jobDataMap=new JobDataMap();jobDataMap.put("jobEntity", job);在同一任務的多次執行之間傳遞數據
3.創建JobDetail實例。JobBuilder.newJob(clazz).withIdentity(getJobKey(job)).usingJobData(jobDataMap).requestRecovery(true).storeDurably(true).build();返回JobDetail實例,newJob(clazz)是要執行特定任務的類;withIdentity(getJobKey(job))是job的任務名和組名;usingJobDatausingJobData(jobDataMap)傳輸數據;
4.創建Trigger實例。TriggerBuilder<CronTrigger> triggerBuilder= TriggerBuilder.newTrigger()
.withIdentity(getTriggerKey(job))
.withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpr())
.withMisfireHandlingInstructionDoNothing());
withIdentity有標示了觸發器的名稱和組(Quartz調度是通過觸發器的類別來識別不同的任務),和withSchedule標示執行的時間表達式
5.最后通過scheduler.scheduleJob()方法調度任務。

