【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 "修改任务成功";
    }
}

方法名删除任务、暂停任务、恢复任务、执行一次任务、修改任务,需要执行一次以后才能完成。

问题

上述方法中,如果暂停后重启任务,会多次执行任务。
image

这里涉及到人misfire 保护机制。默认情况下,中断的任务,会在恢复时继续执行错过的任务。

如果,我们希望暂停时,恢复后忽略所有错误任务,可以在创建或修改任务的cron表达式cronSchedule是添加属性withMisfireHandlingInstructionDoNothing

// 使用新cron表达式构建触发器
CronTrigger newTrigger = oldTrigger.getTriggerBuilder()
	.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing())
	.build();

posted @ 2025-03-25 09:03  陆陆无为而治者  阅读(181)  评论(0)    收藏  举报