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