【Java】【SpringBoot】Quartz——2.动态任务调度
回顾:前面学习了quartz的基础使用:https://www.cnblogs.com/luyj00436/p/18781141
定时任务可能是按照预设的时间进行。可是实际中,我们肯定希望自由的进行任务生成、暂停、恢复、删除、更新等操作。
Quartz本身没有提供动态调度的功能,需要自己根据相关的API开发。
场景
- 订单成功后自动发送短信通知
- 每日推送的功能引发用户不满,不再定时推送
- 每日下班前20分钟发送工作报表,遇节假日暂停发发送。
Scheduler调度器常用API
| API | 说明 |
|---|---|
| scheduler.scheduleJob(jobDetail,trigger) | 生成一个新的定时任务 |
| scheduler.paseJob(jobKey) | 暂停一个定时任务,参数JobKey代表任务的唯一标识 |
| scheduler.resumeJob(jobKey) | 恢复一个定时任务,参数jobKey代表任务的唯一性 |
| scheduler.deleteJob(jobKey) | 删除一个定时任务,参数jobKey代表任务的唯一标识 |
| scheduler.rescheduleJob(triggerKey, trigger) | 修改一个定时任务 |
| scheduler.triggerJob(jobKey) | 执行一次定时任务 |
步骤
新增任务实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JobBean {
/**
* 任务名字
*/
private String jobName;
/**
* 任务类
*/
private String JobClass;
/**
* cron表达式
*/
private String cronExpression;
}
接下来的任务,我们选择根据cron表达式的方式调度。
配置类
@Configuration
public class QuartzConfig {
/**
* 配置任务调度对象
* @return
*/
@Bean
public Scheduler getScheduler() throws SchedulerException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
// 设置任务 和触发器
// scheduler.scheduleJob(jobDetail(),trigger());
// 启动
scheduler.start();
return scheduler;
}
}
接下来,我们新建任务调度的工具类,这样需要动态调度任务时,就可以调度它。
public class JobUtils {
/**
* 创建任务
* @param scheduler 任务调度器
* @param jobBean 任务Bean
*/
public static void createJob(Scheduler scheduler, JobBean jobBean){
JobDetail jobDetail = null; // 任务
Class<? extends Job> jobClass = null;
Trigger trigger = null; // 触发器
try {
// 获取任务
jobClass = Class.forName(jobBean.getJobClass()).asSubclass(Job.class);
jobDetail = JobBuilder.newJob(HelloQuartz.class)
.storeDurably()
.withIdentity(jobBean.getJobName())
.usingJobData("count",1)
.build();
// 获取触发器
trigger = TriggerBuilder.newTrigger()
.withIdentity(jobBean.getJobName())
.withSchedule(CronScheduleBuilder.cronSchedule(jobBean.getCronExpression()))
.build();
scheduler.scheduleJob(jobDetail,trigger);
} catch (SchedulerException e) {
// 调度器异常
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
// 类转换异常
throw new RuntimeException(e);
}
}
/**
* 删除任务
* @param scheduler 任务调度器
* @param jobName 任务名称
*/
public static void deleteJob(Scheduler scheduler,String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.deleteJob(jobKey);
} catch (SchedulerException e) {
// 调度器异常
throw new RuntimeException(e);
}
}
/**
* 暂停任务
* @param scheduler 任务调度器
* @param jobName 任务名称
*/
public static void pauseJob(Scheduler scheduler,String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.pauseJob(jobKey);
} catch (SchedulerException e) {
// 调度器异常
throw new RuntimeException(e);
}
}
/**
* 恢复任务
* @param scheduler
* @param jobName
*/
public static void resumeJob(Scheduler scheduler,String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.resumeJob(jobKey);
} catch (SchedulerException e) {
// 调度器异常
throw new RuntimeException(e);
}
}
/**
* 调用一次接口
* @param scheduler 任务调度器
* @param jobName 任务名称
*/
public static void runJobOnce(Scheduler scheduler,String jobName){
JobKey jobKey = JobKey.jobKey(jobName);
try {
scheduler.triggerJob(jobKey);
} catch (SchedulerException e) {
// 调度器异常
throw new RuntimeException(e);
}
}
/**
* 修改任务
* @param scheduler 任务调度器
* @param jobName 任务名称
* @param cronExpression cron表达式
*/
public static void modifyJob(Scheduler scheduler,String jobName,String cronExpression){
try {
// 通过唯一标识获取旧的触发器对象
CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(jobName));
// 使用新cron表达式构建触发器
CronTrigger newTrigger = oldTrigger.getTriggerBuilder()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
// 调度器更新任务的触发器
scheduler.rescheduleJob(oldTrigger.getKey(),newTrigger);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
}
接下来,创建接口,用于测试。
@RestController("/quartz")
public class QuartzController {
// 使用 @Autowired 注入 Scheduler
@ResourceW
private Scheduler scheduler;
private String crontext = "0/5 * * * * ?";
private String newCrontext = "0/2 * * * * ?";
private String jobName = "job1";
@GetMapping(value = "/createJob")
public String createJob() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobBean jobBean = new JobBean(jobName, HelloQuartz.class.getName(), crontext);
JobUtils.createJob(scheduler, jobBean);
return "创建任务成功";
}
@GetMapping(value = "/deleteJob")
public String deleteJob() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobUtils.deleteJob(scheduler, jobName);
return "删除任务成功";
}
@GetMapping(value = "/pauseJob")
public String pauseJob() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobUtils.pauseJob(scheduler, jobName);
return "暂停任务成功";
}
@GetMapping(value = "/resumeJob")
public String resumeJob() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobUtils.resumeJob(scheduler, jobName);
return "恢复任务成功";
}
@GetMapping(value = "/runJobOnce")
public String runJobOnce() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobUtils.runJobOnce(scheduler, jobName);
return "执行一次任务成功";
}
@GetMapping(value = "/modifyJob")
public String modifyJob() {
if (scheduler == null) {
return "Scheduler 未正确初始化,请检查配置";
}
JobUtils.modifyJob(scheduler, jobName, newCrontext);
return "修改任务成功";
}
}
方法名删除任务、暂停任务、恢复任务、执行一次任务、修改任务,需要执行一次以后才能完成。
问题
上述方法中,如果暂停后重启任务,会多次执行任务。

这里涉及到人misfire 保护机制。默认情况下,中断的任务,会在恢复时继续执行错过的任务。
如果,我们希望暂停时,恢复后忽略所有错误任务,可以在创建或修改任务的cron表达式cronSchedule是添加属性withMisfireHandlingInstructionDoNothing。
// 使用新cron表达式构建触发器
CronTrigger newTrigger = oldTrigger.getTriggerBuilder()
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing())
.build();

有志者,事竟成,破釜沉舟,百二秦关终属楚; 苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

浙公网安备 33010602011771号