回顾:前面学习了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*/@Beanpublic 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@ResourceWprivate 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();