您现在的位置是:首页 >学无止境 >Spring Schedule如何动态添加修改定时任务网站首页学无止境

Spring Schedule如何动态添加修改定时任务

杨某人信了你的邪 2023-06-22 00:00:03
简介Spring Schedule如何动态添加修改定时任务

Spring Schedule如何动态添加修改定时任务

1、快速开始

通常情况下,我们使用的功能很简单,只需要在配置类上加一个@EnableScheduling注解,然后在Bean对应的方法上添加@Scheduled注解即可。但一般情况下,还会自定义对应的线程池等信息,如下所示。

@EnableScheduling
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = threadPoolTaskScheduler();
        taskRegistrar.setScheduler(taskScheduler);
    }

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1);
        scheduler.setThreadNamePrefix("scheduler-");
        scheduler.setRemoveOnCancelPolicy(true);
        return scheduler;
    }
}

@Scheduled注解通常有三种调度方式,fixedRatefixedDelaycron

  • fixedRate:定时执行,如@Scheduled(fixedRate= 2000)会每隔两秒执行一次
  • fixedDelay:固定延迟,如@Scheduled(fixedDelay= 2000)会在上次任务执行完成后,延迟两秒再触发下一次
  • cron:自定义规则,比较复杂,功能更强大

2、Schedule的动态修改

以cron表达式任务为例,在上面的基础上,如果有如下定时任务,在每天凌晨一点执行一次,但是后面发现时间不合适,需要修改触发时间为凌晨两点,按照现有的方式,通常只能修改代码重新部署了。

@Scheduled(cron = "0 0 1 * * ?")
public void foo() {
    // do something
}

可能有人会问,为啥不用Quartz?Quartz自然是非常方便强大的,但不是本篇要讲的内容,本篇就偏要使用SpringSchedule来实现动态的cron表达式任务。

在上面的快速开始一节中,通过configureTasks,我们可以拿到ScheduledTaskRegistrar实例,在这个实例中提供了很多的操作定时任务方法

public ScheduledTask scheduleTriggerTask(TriggerTask task) {/**/}
public ScheduledTask scheduleCronTask(CronTask task) {/**/}
public ScheduledTask scheduleFixedRateTask(IntervalTask task) {/**/} 
public ScheduledTask scheduleFixedRateTask(FixedRateTask task) {/**/}
public ScheduledTask scheduleFixedDelayTask(IntervalTask task) {/**/}
public ScheduledTask scheduleFixedDelayTask(FixedDelayTask task) {/**/}

修改第一步中的配置如下,为了操作简单,这里直接通过ApplicationRunner来进行测试

@Slf4j
@EnableScheduling
@Configuration
public class SchedulerConfig implements SchedulingConfigurer, ApplicationRunner {

    private ScheduledTaskRegistrar taskRegistrar;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = threadPoolTaskScheduler();
        taskRegistrar.setScheduler(taskScheduler);

        this.taskRegistrar = taskRegistrar;
    }

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1);
        scheduler.setThreadNamePrefix("scheduler-");
        scheduler.setRemoveOnCancelPolicy(true);
        return scheduler;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        CronTask cronTask = new CronTask(() -> log.info("foo-----------"), "0/2 * * * * ?");
        ScheduledTask fooTask = taskRegistrar.scheduleCronTask(cronTask);

        ExecutorService executor = Executors.newSingleThreadExecutor(Thread::new);

        executor.execute(() -> {
            try {
                // 等10秒
                TimeUnit.SECONDS.sleep(10);
                Runnable runnable = fooTask.getTask().getRunnable();
                // 停止foo任务
                fooTask.cancel();
                // 重新添加,并修改触发时间为每3秒一次
                taskRegistrar.scheduleCronTask(new CronTask(runnable, "0/3 * * * * ?"));
                // 再添加一个bar任务,每秒执行一次
                taskRegistrar.scheduleCronTask(new CronTask(() -> log.info("bar..."), "0/1 * * * * ?"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

日志如下,从日志中可以看到,上面的操作是成功的,而且也是非常方便的,可以很方便的动态添加定时任务,其余几个方法就不写出来了,感兴趣的可以自己试一下。

2023-04-28 16:38:21.547  INFO 8592 --- [           main] com.example.SpringBootQuartzApplication  : Started SpringBootQuartzApplication in 1.101 seconds (JVM running for 1.563)
2023-04-28 16:38:22.002  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:24.001  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:26.001  INFO 8592 --- [    scheduler-2] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:28.001  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:30.002  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:32.002  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:33.000  INFO 8592 --- [    scheduler-2] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:33.000  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:34.002  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:35.001  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:36.001  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:36.001  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:37.002  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:38.002  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar
2023-04-28 16:38:39.001  INFO 8592 --- [    scheduler-1] com.example.task.SchedulerConfig         : foo-----------
2023-04-28 16:38:39.001  INFO 8592 --- [    scheduler-3] com.example.task.SchedulerConfig         : bar

当然,上面的例子中,因为都是在run方法内,所以没那么多讲究,一般在正式使用的时候,会在scheduleXXXTask返回的ScheduledTask实例保存起来,比如保存到map中并给一个唯一key之类的,以方便后续操作,又或者自定义类实现Runable接口并在其中指定能唯一标识这个任务的方法。具体如何实现,就看具体场景了。

quartz以及xxl-job等框架也是非常优秀的任务调度框架,提供的功能更为强大,但对于比较简单的小项目来说,没有引入的必要,Spring Schedule已经足够用了。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。