Quartz集群模式的定时任务

基于Quartz2.2.x和spirng 4.3.18构建集群模式下的定时任务

引言

由于是集群模式下实现定时任务,Quartz内部是通过使用数据库来作为媒介,进行消息的通知,因为所有的机器会操作同一个库,这样不会造成定时任务的混乱。

一、引入maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.18.RELEASE</version>
</dependency>

<!-- quartz 依赖-->
<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.2.1</version>
</dependency>
<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz-jobs</artifactId>
  <version>2.2.1</version>
</dependency>  
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.36</version>
</dependency>

二、建立与Quartz相关的数据库表格

这个数据库的DDL语句Quartz官方有给出,根据你所使用的数据库来选择对应的DDL

点击查看

我选择的MYSQL数据库,注意使用mysql一定要切换成innodb引擎、

下面给出对应的建表语句,一定要先执行建表语句,再执行创建索引的语句,不然就会产生错误。

#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;



#创建索引,先建表再建索引。


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);

commit;


三、建立属性文件spring-quartz.properties

一般情况下,可以直接在存放资源文件的目录下建立spring-quartz.properties属性文件,用于存放quartz的相关配置,如果使用的idea开发,直接放在resources文件夹目录下即可,该目录对应的就是classpath,可以很方便的索引到文件。

#配置见http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/ConfigJDBCJobStoreClustering.html  
#=====================================================================
#Configure Main Scheduler Properties    
#============================================================================  

org.quartz.scheduler.instanceName = MyClusteredScheduler  
org.quartz.scheduler.instanceId = AUTO  

#============================================================================  
#Configure ThreadPool    
#============================================================================  

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool  
org.quartz.threadPool.threadCount = 25  
org.quartz.threadPool.threadPriority = 5  

#======================================================================
#Configure JobStore    
#======================================================================

org.quartz.jobStore.misfireThreshold = 60000  

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX  
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
org.quartz.jobStore.useProperties = false  
org.quartz.jobStore.dataSource = myDS  
org.quartz.jobStore.tablePrefix = QRTZ_  

org.quartz.jobStore.isClustered = true  
org.quartz.jobStore.clusterCheckinInterval = 20000


四、整合spirng

为了解决Spring quartz Job不能依赖注入。需要自定义job工厂类

import org.quartz.spi.TriggerFiredBundle;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;  
import org.springframework.scheduling.quartz.SpringBeanJobFactory;  

public class CustomJobFactory extends SpringBeanJobFactory{  

    @Autowired    
    private AutowireCapableBeanFactory capableBeanFactory;    

    @Override    
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {    
        //调用父类的方法    
        Object jobInstance = super.createJobInstance(bundle);    
        //进行注入    
        capableBeanFactory.autowireBean(jobInstance);    
        return jobInstance;    
    }  

}  

spirng-quartz.xml

<!--这里定义你自己的包的全限定名-->
<bean id="customJobFactory" class="xx.xx.xx.job.CustomJobFactory"></bean>  
     <!-- 定时任务配置 start -->  
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">    
        <property name="dataSource" ref="dataSource"></property>      
        <!-- 可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->        
        <property name="overwriteExistingJobs" value="true" />        
         <!-- 必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动  -->     
        <property name="startupDelay" value="2" />      
        <!-- 设置自动启动  -->     
        <property name="autoStartup" value="true" />    
        <property name="jobFactory" ref="customJobFactory"></property>  
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />  
        <property name="configLocation" value="classpath:spring-quartz.properties" />        
    </bean>  
    <!-- 定时任务配置 end -->  

五、封装一个定时任务的操作类

为了方便解耦,面向接口编程,先定义一个SchedulerManage接口,具体实现SchedulerManageImpl

ScheduleJob

封装一些定时任务需要的信息

import java.io.Serializable;  
import java.util.Date;  

@SuppressWarnings("serial")  
public class ScheduleJob implements Serializable{  

    private Long scheduleJobId;  

    /** 任务名称 */  
    private String jobName;  

    /** 任务分组 */  
    private String jobGroup;  

    /** 定时任务对应的类(包括包路径) */  
    private String clazz;  

    /** 任务状态:1禁用 2启用*/  
    private String jobStatus;  

    /** 任务运行时间表达式 */  
    private String cronExpression;  

    /** 简单的时间值 */  
    private Integer timeValue;  
    /** 时间类型:秒、分、小时、天 */  
    private String timeType;  

    /** 任务描述 */  
    private String jobDesc;  



    public Long getScheduleJobId() {  
        return scheduleJobId;  
    }  

    public void setScheduleJobId(Long scheduleJobId) {  
        this.scheduleJobId = scheduleJobId;  
    }  

    public String getJobName() {  
        return jobName;  
    }  

    public void setJobName(String jobName) {  
        this.jobName = jobName;  
    }  

    public String getJobGroup() {  
        return jobGroup;  
    }  

    public void setJobGroup(String jobGroup) {  
        this.jobGroup = jobGroup;  
    }  

    public String getClazz() {  
        return clazz;  
    }  

    public void setClazz(String clazz) {  
        this.clazz = clazz;  
    }  

    public String getJobStatus() {  
        return jobStatus;  
    }  

    public void setJobStatus(String jobStatus) {  
        this.jobStatus = jobStatus;  
    }  

    public String getCronExpression() {  
        return cronExpression;  
    }  

    public void setCronExpression(String cronExpression) {  
        this.cronExpression = cronExpression;  
    }  

    public Integer getTimeValue() {  
        return timeValue;  
    }  

    public void setTimeValue(Integer timeValue) {  
        this.timeValue = timeValue;  
    }  

    public String getTimeType() {  
        return timeType;  
    }  

    public void setTimeType(String timeType) {  
        this.timeType = timeType;  
    }  

    public String getJobDesc() {  
        return jobDesc;  
    }  

    public void setJobDesc(String jobDesc) {  
        this.jobDesc = jobDesc;  
    }       
}  

SchedulerManage

import java.util.List;  
public interface SchedulerManage {  
    /**
     * 新增任务
     * @param scheduleJob
     * @throws Exception  
     */  
    public void addJobCron(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 暂停任务
     * @param scheduleJob
     */  
    public void pauseJob(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 暂停全部任务
     */  
    public void pauseAll() throws Exception;  

    /**
     * 恢复任务
     * @param scheduleJob
     */  
    public void resumeJob(ScheduleJob scheduleJob) throws Exception;  


    /**
     * 恢复所有任务
     */  
    public void resumeAll() throws Exception;  

    /**
     * 删除任务后,所对应的trigger也将被删除
     * @param scheduleJob
     */  
    public void deleteJob(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 立即运行任务
     * @param scheduleJob
     */  
    public void triggerJob(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 更新任务的时间表达式
     * @param scheduleJob
     */  
    public void updateJobCron(ScheduleJob scheduleJob) throws Exception;  


    /**
     * 获取quartz调度器的计划任务
     * @return
     */  
    public List<ScheduleJob> getScheduleJobList();  

    /**
     * 获取quartz调度器的运行任务
     * @return
     */  
    public List<ScheduleJob> getScheduleJobRunningList();  

    public void addJobSimple(ScheduleJob scheduleJob) throws Exception;  

    public void updateJobSimple(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 如果scheduleJob.getCronExpression()表达式不为空,使用表达式方式,如果为空,则使用简单方式
     * @param scheduleJob
     */  
    public void add(ScheduleJob scheduleJob) throws Exception;  

    /**
     * 如果scheduleJob.getCronExpression()表达式不为空,使用表达式方式,如果为空,则使用简单方式
     * @param scheduleJob
     */  
    public void update(ScheduleJob scheduleJob) throws Exception;  

SchedulerManageImpl

import java.util.ArrayList;  
import java.util.Date;  
import java.util.List;  
import java.util.Set;  

import org.quartz.CronScheduleBuilder;  
import org.quartz.CronTrigger;  
import org.quartz.Job;  
import org.quartz.JobBuilder;  
import org.quartz.JobDetail;  
import org.quartz.JobExecutionContext;  
import org.quartz.JobKey;  
import org.quartz.Scheduler;  
import org.quartz.SchedulerException;  
import org.quartz.SimpleScheduleBuilder;  
import org.quartz.SimpleTrigger;  
import org.quartz.Trigger;  
import org.quartz.TriggerBuilder;  
import org.quartz.TriggerKey;  
import org.quartz.impl.matchers.GroupMatcher;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Repository;  

@Repository  
public class SchedulerManageImpl implements SchedulerManage{  

    private Logger log = LoggerFactory.getLogger(SchedulerManageImpl.class);  

    @Autowired  
    private Scheduler scheduler;  

    /**
     * 新增任务
     * @param scheduleJob
     */  
    @Override  
    public void add(ScheduleJob scheduleJob)  throws Exception{  
        if(!StrUtils.isBlank(scheduleJob.getCronExpression())){  
            this.addJobCron(scheduleJob);  
        }else{  
            this.addJobSimple(scheduleJob);  
        }  
    }  

    /**
     * 更新任务
     * @param scheduleJob
     */  
    @Override  
    public void update(ScheduleJob scheduleJob)  throws Exception{  
        if(!StrUtils.isBlank(scheduleJob.getCronExpression())){  
            this.updateJobCron(scheduleJob);  
        }else{  
            this.updateJobSimple(scheduleJob);  
        }  
    }  

    /**
     * 新增任务
     * @param scheduleJob
     * @throws Exception  
     */  
    @SuppressWarnings("unchecked")  
    @Override  
    public void addJobCron(ScheduleJob scheduleJob) throws Exception{  
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());  
        //任务触发  
        Trigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);  
        if (null == trigger) {  
            JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getClazz()))  
              .withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();  
            jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);  
            //这里需要纪录class方便,后续的反射调用
            jobDetail.getJobDataMap().put("class", scheduleJob.getClazz());  
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());  
            /*withMisfireHandlingInstructionDoNothing
            ——不触发立即执行
            ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行

            withMisfireHandlingInstructionIgnoreMisfires
            ——以错过的第一个频率时间立刻开始执行
            ——重做错过的所有频率周期后
            ——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行

            withMisfireHandlingInstructionFireAndProceed
            ——以当前时间为触发频率立刻触发一次执行
            ——然后按照Cron频率依次执行*/  
            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(cronScheduleBuilder.withMisfireHandlingInstructionDoNothing()).build();  
            scheduler.scheduleJob(jobDetail, trigger);  
            log.info("新增Cron任务:"+scheduleJob.toString());  
        }else {  
            // Trigger已存在,那么更新相应的定时设置  
            this.updateJobCron(scheduleJob);  
        }  
    }  

    /**
     * 更新任务的时间表达式
     * @param scheduleJob
     */  
    @Override  
    public void updateJobCron(ScheduleJob scheduleJob) throws Exception{
        this.deleteJob(scheduleJob);  
        this.addJobCron(scheduleJob);  
        log.info( "更新Cron任务(先删除再更新):"scheduleJob.toString());  
    }  

    /**
     * 新增任务
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    @SuppressWarnings("unchecked")  
    public void addJobSimple(ScheduleJob scheduleJob) throws Exception{  
        //暂时没有使用到simple,一般都是使用cron表达式
    }  

    /**
     * 更新任务的时间表达式
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    public void updateJobSimple(ScheduleJob scheduleJob) throws Exception{  
        // this.deleteJob(scheduleJob);  
        // this.addJobSimple(scheduleJob);  

    }  



    /**
     * 暂停任务
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    public void pauseJob(ScheduleJob scheduleJob) throws Exception{  
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());  
        scheduler.pauseJob(jobKey);  
        log.info( "暂停任务:"+scheduleJob.toString());  
    }  

    /**
     * 暂停全部任务
     * @throws SchedulerException  
     */  
    @Override  
    public void pauseAll() throws Exception{  
        scheduler.pauseAll();  
        log.info("暂停所有任务");  
    }  

    /**
     * 恢复任务
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    public void resumeJob(ScheduleJob scheduleJob) throws Exception{  
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());  
        scheduler.resumeJob(jobKey);  
        log.info( "恢复任务:"+scheduleJob.toString());  
    }  


    /**
     * 恢复所有任务
     * @throws Exception  
     */  
    @Override  
    public void resumeAll() throws Exception{  
        scheduler.resumeAll();  
        log.info("恢复所有任务");  
    }  

    /**
     * 删除任务后,所对应的trigger也将被删除
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    public void deleteJob(ScheduleJob scheduleJob) throws Exception{  
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());  
        scheduler.pauseJob(jobKey);//先暂停任务  
        scheduler.deleteJob(jobKey);//再删除任务  
        log.info( "删除任务:"+scheduleJob.toString());  
    }  

    /**
     * 立即运行任务
     * @param scheduleJob
     * @throws Exception  
     */  
    @Override  
    public void triggerJob(ScheduleJob scheduleJob) throws Exception{  
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());  
        scheduler.triggerJob(jobKey);  
        log.info("运行任务:"+scheduleJob.toString());  
    }  


    /**
     * 获取quartz调度器的计划任务
     * @return
     */  
    @Override  
    public List<ScheduleJob> getScheduleJobList(){  
        List<ScheduleJob> jobList = null;  
        try {  
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();  
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);  
            jobList = new ArrayList<ScheduleJob>();  
            for (JobKey jobKey : jobKeys) {  
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);  
                for (Trigger trigger : triggers) {  
                    ScheduleJob job = new ScheduleJob();  
                    job.setJobName(jobKey.getName());  
                    job.setJobGroup(jobKey.getGroup());  

                    job.setJobDesc("触发器:" + trigger.getKey());  
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());  
                    job.setJobStatus(triggerState.name());

                    job.setClazz(jobKey.getClass().toString());   
                    if (trigger instanceof CronTrigger) {  
                        CronTrigger cronTrigger = (CronTrigger) trigger;  
                        String cronExpression = cronTrigger.getCronExpression();  
                        job.setCronExpression(cronExpression);  
                    }else if(trigger instanceof SimpleTrigger){  
                        SimpleTrigger simpleTrigger = (SimpleTrigger) trigger;  
                        long milliseconds = simpleTrigger.getRepeatInterval();  
                        job.setTimeValue((int) (milliseconds/1000));  
                    }  
                    jobList.add(job);  
                }  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return jobList;  
    }  

    /**
     * 获取quartz调度器的运行任务
     * @return
     */  
    @Override  
    public List<ScheduleJob> getScheduleJobRunningList(){  
        List<ScheduleJob> jobList = null;  
        try {  
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();  
            jobList = new ArrayList<ScheduleJob>(executingJobs.size());  
            for (JobExecutionContext executingJob : executingJobs) {  
              ScheduleJob job = new ScheduleJob();  
              JobDetail jobDetail = executingJob.getJobDetail();  
              JobKey jobKey = jobDetail.getKey();  
              Trigger trigger = executingJob.getTrigger();  
              job.setJobName(jobKey.getName());  
              job.setJobGroup(jobKey.getGroup());  
              job.setClazz(jobDetail.getJobDataMap.get("class"));  
              job.setJobDesc("触发器:" + trigger.getKey());  
              Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());  
              job.setJobStatus(triggerState.name());  
              if (trigger instanceof CronTrigger) {  
                  CronTrigger cronTrigger = (CronTrigger) trigger;  
                  String cronExpression = cronTrigger.getCronExpression();  
                  job.setCronExpression(cronExpression);  
              }else if(trigger instanceof SimpleTrigger){  
                  SimpleTrigger simpleTrigger = (SimpleTrigger) trigger;  
                  long milliseconds = simpleTrigger.getRepeatInterval();  
                  job.setTimeValue((int) (milliseconds/1000));  
              }  
              jobList.add(job);  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return jobList;  
    }  



}  

注意事项:

  • 每个任务类必须实现序列化接口
  • 产生NotSerializableException
java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean

解决办法:地址,上面有解决的MethodInvokingJobDetailFactoryBean代码。

但是我又看spirng官方给出的说明:

NOTE: JobDetails created via this FactoryBean are not serializable and thus not suitable for persistent job stores. You need to implement your own Quartz Job as a thin wrapper for each case where you want a persistent job to delegate to a specific service method.

Compatible with Quartz 1.5+ as well as Quartz 2.0-2.2, as of Spring 3.2.

使用MethodInvokingJobDetailFactoryBean工厂类创建的JobDetail是无法被序列化的,这就意味着无法进行持久化操作,所以建议还是自己手动创建一个Job类的实现Job,Serializable接口。

  • Jobs added with no trigger must be durable

没有给job添加触发器,你可以在配置文件中加上durable属性即可。// durable, 指明任务就算没有绑定Trigger仍保留在Quartz的JobStore中.

解决办法:

<property name="durability" value="true" />
  • 持久化时jobDetail找不到
org.quartz.JobPersistenceException: The job (DEFAULT.studyDetail) referenced by the trigger does not exist.

原因一般是数据源配置导致的问题。

可能的原因是:数据源未配置成自动提交,当第一次启动trigger时,之前对数据库的job的增加的事物没有自动提交,导致后面的事物无法查询到。
如果数据源是通过dbcp配置的将自动提交配置为true

<property name="defaultAutoCommit" value="false" />

终于搞定了,Quartz的集群基本上就是这些问题了,今天折腾了一上午,看了下百度,google上面的基本上不怎么全,有些地方都写的不怎么完整,还是自己结合网上的解释然后根据源码大致总结了这些,希望能够用得到。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值