Spring Boot集成Quartz
我们常用的定时任务调度有如下几种:
1. 使用JDK自带的类库实现
- 通过继承TimeTask,使用Time来调度任务
- 使用ScheduledExecutorService来实现任务调度
2,Spring 自带了任务调度功能,通过使用@Schedule()注解来实现
3,使用Quartz框架,Quartz可以用于在分布式环境下的任务调度
Quartz的基本概念:
1,Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:
void execute(JobExecutionContext context)
2,JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
3,Trigger 代表一个调度参数的配置,什么时候去调。
4,Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
配置环境依赖
添加Maven依赖:
org.springframework.boot
spring-boot-starter-quartz
修改application.yml文件,添加:
spring:
datasource:
name: schedule
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/schedule?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
quartz:
job-store-type: jdbc
quartz支持两种存储方式,一个使用数据库,也就是job-store-type=jdbc,需要你下载quartz提供的表,并导入到你本地,然后配置你的数据库连接,官方下载地址, 另一种是内存方式,job-store-type设置为momery
配置任务
创建一个任务配置,这里的数据可以对应数据库一条记录
public class ScheduleTaskConfig {
private Integer id;
private String name;
private String groupName;
private String describe;
private String cron;
private String classPath;
private Character isEnabled;
private String createTime;
private String updateTime;
public String getClassName() {
try {
Class cls = Class.forName(classPath);
return cls.getSimpleName();
} catch (ClassNotFoundException e) {
log.error("获取Class失败",e);
}
return null;
}
}
创建用于操作Quartz任务创建、暂停、恢复方法
Service
@Slf4j
public class QuartzService {
@Autowired
private Scheduler scheduler;
public void createJob(ScheduleTaskConfig config) {
Class cls = null;
try {
cls = Class.forName(config.getClassPath());
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("config", config);
JobDetail jobDetail = JobBuilder.newJob(cls)
.withIdentity(config.getName(), config.getGroupName())
.withDescription(config.getDescribe())
.setJobData(jobDataMap)
.storeDurably()
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(), GROUP_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule(config.getCron()))
.build();
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
log.error("创建任务失败", e);
}
}
/**
* 获取任务状态
* @return
*/
public String getJobState(ScheduleTaskConfig config) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
return state.name();
} catch (Exception e) {
log.error("创建任务失败", e);
}
return null;
}
/**
* 判断Job是否存在
* @param config
* @return
*/
public boolean isExistJob(ScheduleTaskConfig config) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
if (trigger != null) {
return true;
}
} catch (Exception e) {
log.error("获取任务trigger失败", e);
}
return false;
}
/**
* 更新Job任务
* @param config
*/
public void updateJob(ScheduleTaskConfig config) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(config.getName(), config.getGroupName());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(config.getCron());
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
// 忽略状态为PAUSED的任务,解决集群环境中在其他机器设置定时任务为PAUSED状态后,集群环境启动另一台主机时定时任务全被唤醒的bug
if(!triggerState.name().equalsIgnoreCase("PAUSED")){
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
}
} catch (Exception e) {
log.error("更新任务trigger失败", e);
}
}
/**
* 停止任务
* @param jobName
* @param groupName
* @return
*/
public boolean pauseJob(String jobName) {
try {
JobKey jobKey = JobKey.jobKey(config.getName(), config.getGroupName());
scheduler.pauseJob(jobKey);
return true;
} catch (SchedulerException e) {
log.error("停止任务失败", e);
}
return false;
}
/**
* 恢复任务
* @param jobName
* @param groupName
* @return
*/
public boolean resumeJob(String jobName) {
try {
JobKey jobKey = JobKey.jobKey(config.getName(), config.getGroupName());
scheduler.resumeJob(jobKey);
return true;
} catch (SchedulerException e) {
log.error("恢复任务失败", e);
}
return false;
}
/**
* 删除任务
* @param jobName
* @param groupName
* @return
*/
public boolean deleteJob(String jobName) {
try {
JobKey jobKey = JobKey.jobKey(config.getName(), config.getGroupName());
scheduler.deleteJob(jobKey);
return true;
} catch (SchedulerException e) {
log.error("删除任务失败", e);
}
return false;
}
可以看到Quartz创建任务就是通过JobDetail和Trigger来实现定时任务的,JobDetail声明了任务的一些信息,比如名称、分组、描述、需要执行类(例如:com.chuanz.task.CheckExecutor)需要的数据(通过调用setJobData方法) Trigger设置了任务的执行周期及触发时间,这里通过cron表达式来设置触发,另外Trigger还有另外一种触发方式,通过SimpleScheduleBuilder来实现,比如:
.withSchedule(SimpleScheduleBuilder.repeatMinutelyForTotalCount(10, 5))
我需要重复执行10次,每隔5分钟执行一次
创建我们需要执行的任务,实现Job接口,并重写execute方法
@Slf4j
@Service
public class CheckExecutor implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("执行计划冲突类型审核开始");
// 获取数据
ScheduleTaskConfig config = (ScheduleTaskConfig)context.get("config");
/**
* 处理逻辑
*/
log.info("执行计划冲突类型审核结束");
}
}
我们可以将quartz创建、删除、停止、恢复任务都封装成一个个接口,通过前端页面来管理quartz的任务,具体前端页面代码我就不贴了,基本就是调用QuartzService里面的方法了,另外说说获取quartz任务列表的时候,我们可以获取Trigger里面响应的执行状态,通过Trigger里面的方法来获取,比如:
JobKey jobKey = new JobKey(config.getName(), config.getGroupName());
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
if (triggers.size() > 0) {
Trigger trigger = triggers.get(0);
//开始执行时间
trigger.getStartTime();
//上一次执行时间
trigger.getPreviousFireTime();
//下一次执行时间
trigger.getNextFireTime();
}
trigger有很多实现,比如我们这里使用的是cron表达式,所以相对应的就是CronTriggerImpl,常用的触发器有四种。
触发器介绍
SimpleTrigger:简单的触发器
指定从某一个时间开始,以一定的时间间隔,如秒、分、小时重复执行的任务
比如:从10:00 开始,每隔1小时/1分钟/1秒钟执行一次,执行10次。
对应的方法如下:
CalendarIntervalTrigger:日历触发器
类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
对应的方法如下:
DailyTimeIntervalTrigger:日期触发器
指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。 它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。 比如如下代码:
DailyTimeIntervalTrigger trigger = dailyTimeIntervalSchedule()
.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(10, 0)) //从10:00点开始
.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(22, 0)) //22:00点结束
.onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一到周五执行
.withIntervalInHours(1) //每间隔1小时执行一次
.withRepeatCount(50) //最多重复50次(实际执行50+1次)
.build();
CronTrigger:Cron表达式触发器
适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力
属性只有一个就是cron表达式
我们可以根据对应的触发器取到任务执行的数据,如之前用SimpleScheduleBuilder.repeatMinutelyForTotalCount(10, 5),那么对应的就是SimpleTrigger,我们可以获取执行的次数,剩余的次数等。 触发器的介绍可以参考官方文档

