springBoot集成Quartz实现任务调度

quartz是一个很强大的任务调度插件,上一节已经讲述了springBoot自带的任务调度功能,但是只能满足简单的需求,集成quartz是为了满足更复杂的需求,比如需要执行的任务存到数据库中保存,包括配置任务信息触发器信息等,再比如需要对执行的任务随时暂停,删除,修改执行频率,执行参数等,而且也要随时添加新的任务去执行,这种复杂的需求下,简单的线程池或者定时任务就很难满足需求,所以就需要quartz来方便我们的使用。

虽然quartz很强大感觉很复杂,但是使用起来其实很简单的。

quartz表结构如下,mysql的数据库

create table if not exists apg.QRTZ_CALENDARS
(
	SCHED_NAME varchar(120) not null,
	CALENDAR_NAME varchar(200) not null,
	CALENDAR blob not null,
	primary key (SCHED_NAME, CALENDAR_NAME)
);

create table if not exists apg.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 int 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)
);

create index IDX_QRTZ_FT_INST_JOB_REQ_RCVRY
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);

create index IDX_QRTZ_FT_JG
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);

create index IDX_QRTZ_FT_J_G
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);

create index IDX_QRTZ_FT_TG
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);

create index IDX_QRTZ_FT_TRIG_INST_NAME
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);

create index IDX_QRTZ_FT_T_G
	on apg.QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table if not exists apg.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)
);

create index IDX_QRTZ_J_GRP
	on apg.QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);

create index IDX_QRTZ_J_REQ_RECOVERY
	on apg.QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);

create table if not exists apg.QRTZ_LOCKS
(
	SCHED_NAME varchar(120) not null,
	LOCK_NAME varchar(40) not null,
	primary key (SCHED_NAME, LOCK_NAME)
);

create table if not exists apg.QRTZ_PAUSED_TRIGGER_GRPS
(
	SCHED_NAME varchar(120) not null,
	TRIGGER_GROUP varchar(200) not null,
	primary key (SCHED_NAME, TRIGGER_GROUP)
);

create table if not exists apg.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)
);

create table if not exists apg.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 int 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),
	constraint QRTZ_TRIGGERS_ibfk_1
		foreign key (SCHED_NAME, JOB_NAME, JOB_GROUP) references apg.QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
);

create table if not exists apg.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),
	constraint QRTZ_BLOB_TRIGGERS_ibfk_1
		foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);

create index SCHED_NAME
	on apg.QRTZ_BLOB_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table if not exists apg.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) null,
	primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
	constraint QRTZ_CRON_TRIGGERS_ibfk_1
		foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);

create table if not exists apg.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),
	constraint QRTZ_SIMPLE_TRIGGERS_ibfk_1
		foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);

create table if not exists apg.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 decimal(13,4) null,
	DEC_PROP_2 decimal(13,4) null,
	BOOL_PROP_1 varchar(1) null,
	BOOL_PROP_2 varchar(1) null,
	primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
	constraint QRTZ_SIMPROP_TRIGGERS_ibfk_1
		foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);

create index IDX_QRTZ_T_C
	on apg.QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);

create index IDX_QRTZ_T_G
	on apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);

create index IDX_QRTZ_T_J
	on apg.QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);

create index IDX_QRTZ_T_JG
	on apg.QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);

create index IDX_QRTZ_T_NEXT_FIRE_TIME
	on apg.QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);

create index IDX_QRTZ_T_NFT_MISFIRE
	on apg.QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);

create index IDX_QRTZ_T_NFT_ST
	on apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);

create index IDX_QRTZ_T_NFT_ST_MISFIRE
	on apg.QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);

create index IDX_QRTZ_T_NFT_ST_MISFIRE_GRP
	on apg.QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);

create index IDX_QRTZ_T_N_G_STATE
	on apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);

create index IDX_QRTZ_T_N_STATE
	on apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);

create index IDX_QRTZ_T_STATE
	on apg.QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);

1、在pom中添加quartz的依赖

<!-- quartz集成 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2、在项目中创建一个util包,并编写一个任务调度关联工具类,对job增删改查等操作

package com.apgblogs.springbootstudy.util;

import org.quartz.*;
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.Component;

import java.util.*;

/**
 * @author xiaomianyang
 * @description
 * @date 2019-07-17 16:24
 */
@Component
public class QuartzJobUtil {

    private static final Logger logger = LoggerFactory.getLogger(QuartzJobUtil.class);

    private static QuartzJobUtil quartzJobUtil;

    @Autowired
    private Scheduler scheduler;
    
    public QuartzJobUtil() {
        logger.info("init jobUtil");
        quartzJobUtil = this;
    }

    public static QuartzJobUtil getInstance() {
        logger.info("return  JobCreateUtil");
        return QuartzJobUtil.quartzJobUtil;
    }

    /**
     * @description 创建job
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [clazz, jobName, jobGroupName, cronExpression]
     * @return void
     */
    public void addJob(Class clazz, String jobName, String jobGroupName, String cronExpression) {
        addJob(clazz, jobName, jobGroupName, cronExpression, null);
    }


    /**
     * @description 创建job
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [clazz, jobName, jobGroupName, cronExpression, argMap]
     * @return void
     */
    public void addJob(Class clazz, String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) {
        try {
            // 启动调度器
            scheduler.start();
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(((Job) clazz.newInstance()).getClass()).withIdentity(jobName, jobGroupName).build();
            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(scheduleBuilder).build();
            //获得JobDataMap,写入数据
            if (argMap != null) {
                trigger.getJobDataMap().putAll(argMap);
            }
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @description 暂停job
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [jobName, jobGroupName]
     * @return void
     */
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * @description 恢复job
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [jobName, jobGroupName]
     * @return void
     */
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }


    /**
     * @description 更新job频率
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [jobName, jobGroupName, cronExpression]
     * @return void
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression) {
        updateJob(jobName, jobGroupName, cronExpression, null);


    }


    /**
     * @description 更新job频率和参数
     * @author xiaomianyang
     * @date 2019-07-17 17:31
     * @param [jobName, jobGroupName, cronExpression, argMap]
     * @return void
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //修改map
            if (argMap != null) {
                trigger.getJobDataMap().putAll(argMap);
            }
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @description 更新job参数
     * @author xiaomianyang
     * @date 2019-07-17 17:30
     * @param [jobName, jobGroupName, argMap]
     * @return void
     */
    public void updateJob(String jobName, String jobGroupName, Map<String, Object> argMap) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //修改map
            trigger.getJobDataMap().putAll(argMap);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * @description 删除job
     * @author xiaomianyang
     * @date 2019-07-17 17:30
     * @param [jobName, jobGroupName]
     * @return void
     */
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * @description 启动所有定时任务
     * @author xiaomianyang
     * @date 2019-07-17 17:30
     * @param []
     * @return void
     */
    public void startAllJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @description 关闭所有定时任务
     * @author xiaomianyang
     * @date 2019-07-17 17:30
     * @param []
     * @return void
     */
    public void shutdownAllJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @description 获取所有任务列表
     * @author xiaomianyang
     * @date 2019-07-17 17:30
     * @param []
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
     */
    public List<Map<String, Object>> getAllJob() {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        List<Map<String, Object>> jobList = new ArrayList<>();
        Set<JobKey> jobKeys = null;
        try {
            jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> job = new HashMap<>();
                    job.put("jobName", jobKey.getName());
                    job.put("jobGroupName", jobKey.getGroup());
                    job.put("trigger", trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.put("cronExpression", cronExpression);
                    }
                    jobList.add(job);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        return jobList;
    }
}

3、在项目中创建一个task包,用来编写需要执行的任务类

package com.apgblogs.springbootstudy.task;

import com.apgblogs.springbootstudy.service.UserServiceImpl;
import com.google.gson.Gson;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author xiaomianyang
 * @description
 * @date 2019-07-16 16:07
 */
public class TaskOne extends QuartzJobBean {

    private final Logger logger= LoggerFactory.getLogger(TaskOne.class);

    @Autowired
    private UserServiceImpl userService;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) {
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        logger.info("用户列表:{}",new Gson().toJson(userService.getUserList()));
        logger.info("任务1执行:{}",simpleDateFormat.format(new Date()));
    }

4、在配置文件中修改quartz的存储类型为jdbc,默认是内存存储

......
quartz:
 job-store-type: jdbc
......

5、编写测试用例

由于测试用例直接就跑完了,所以休眠个一段时间,就可以看到任务输出了,每秒输出一次,这里的先进行了删除,如果任务已经有了就可以删除重新添加,跟数据库是同步的,实时修改和创建任务数据

    @Test
    public void addJob() throws Exception{
        quartzJobUtil.deleteJob("我的job","task");
        quartzJobUtil.addJob(TaskOne.class,"我的job","task","* * * * * ?");
        Thread.sleep(100000);
    }

    @Test
    public void pauseJob(){
        quartzJobUtil.resumeJob("我的job","task");
    }

6、可以启动测试看看结果

springBoot集成Quartz实现任务调度

7、文章源码地址

码云:https://gitee.com/apgblogs/springBootStudy/tree/quartz/

quartz就已经集成上去了,有任何需要定时执行或者由用户手动创建添加的任务都可以用quartz实现,采用的数据库存储,对于已经添加好的job也就不用在重新添加了,只要项目每次启动就会根据定时计划继续执行,对jobDetail表映射实体类,也可以用jpa去查询任务信息,是不是很方便呢。

发表评论