文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

探索动态执行的计划任务-DynamicSchedule

2024-11-28 15:27

关注

最近有一个需求,用户想要自己设定定时时间,来动态的执行定时任务。 很离谱,原来每天晚上12点定时执行的几个数据同步、数据清理任务,想不通用户要这个功能干啥!!!

探索历程

原本的cron表达式,是直接写死到代码里的,显然不能动态的修改。

如果采用配置文件的方式,每次改动要重启项目,或者再写个定时任务,每秒读取文件内容,也不太合适。

如果引入分布式任务调度平台,比如xxl-job、power-job、snail-job,又觉得太复杂。

选择采用放到数据库的方式,实现过程中,发现并不是很顺利,写一篇文章记录一下这次的过程。

原本的实现

@Scheduled(cron = "0/5 * * * * *")  
public void demo() {  
    System.out.println(LocalDateTime.now());  
}

结果

图片图片

动态设置

配置类

@Component  
@RequiredArgsConstructor  
public class JobConfig implements SchedulingConfigurer {  
  
    private final ITestJobService jobService;  
  
    @Override  
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {  
        taskRegistrar.addTriggerTask(  
                //1.添加任务内容(Runnable)  
                () -> System.out.println("执行动态定时任务1: " + LocalDateTime.now()),  
                //2.设置执行周期(Trigger)  
                triggerContext -> {  
                    TestJob job = jobService.getById(1L);  
                    return new CronTrigger(job.getCron()).nextExecutionTime(triggerContext).toInstant();  
                }  
        );  
    }  
}

修改入口

@GetMapping("upd")  
public String upd(@RequestParam("cron") String cron) {  
    jobService.updateById(new TestJob(1, cron));  
    System.out.println("修改时间:"+ LocalDateTime.now());  
    return "success";  
}

将 0/10 * * * * * 改为 0/5 * * * * *

结果

图片图片

可以看出来 修改的时间是 15:01 ,但是下次执行时间还是间隔了10秒,第二次之后的时间才是间隔5秒。 更新结果有一个周期的延迟。

在这种情况下,延迟还算可以接收,但是周期如果是一天、一周,那生效周期就太长了,需要一种即时生效的方法。

即时生效

实现方案是,以事件驱动,动态修改定时任务。

定义事件

@Getter  
public class ScheduleTaskUpdateEvent extends ApplicationEvent {  
  
    private final Integer taskId;  
  
    public ScheduleTaskUpdateEvent(Object source, Integer taskId) {  
        super(source);  
        this.taskId = taskId;  
    }  
}

构造调度任务程序

@Configuration  
public class SchedulerConfig {  
  
    @Bean  
    public ThreadPoolTaskScheduler taskScheduler() {  
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();  
        scheduler.setPoolSize(10); // 设置线程池大小  
        scheduler.setThreadNamePrefix("scheduled-task-");  
        scheduler.initialize();  
        return scheduler;  
    }  
}

动态任务配置

@Component  
public class DynamicScheduleTaskConfig implements ApplicationListener {  
  
    @Resource  
    private ITestJobService jobService;  
  
    @Resource  
    private TaskScheduler taskScheduler;  
  
    private final Map> scheduledTasks = new ConcurrentHashMap<>();  
  
    @PostConstruct  
    private void initializeTasks() {  
        List list = jobService.list();  
        list.forEach(job -> {  
            ScheduledFuture future = scheduleTask(job);  
            scheduledTasks.put(job.getId(), future);  
        });  
    }  
  
    // 根据任务配置创建任务  
    private ScheduledFuture scheduleTask(TestJob job) {  
        System.out.println("创建新的定时任务,id:" + job.getId() + ", cron: " + job.getCron());  
        return taskScheduler.schedule(  
                () -> System.out.println("执行动态定时任务2: " + LocalDateTime.now()),  
                triggerContext -> {  
                    return new CronTrigger(job.getCron()).nextExecutionTime(triggerContext).toInstant();  
                }  
        );  
    }  
  
    @Override  
    public void onApplicationEvent(ScheduleTaskUpdateEvent event) {  
        System.out.println("收到修改定时任务事件,任务id:" + event.getTaskId());  
        // 取消并移除旧任务  
        ScheduledFuture future = scheduledTasks.get(event.getTaskId());  
        if (future != null) {  
            future.cancel(false);  
            scheduledTasks.remove(event.getTaskId());  
        }  
  
        // 获取最新的任务配置并重新注册该任务  
        TestJob job = jobService.getById(event.getTaskId());  
        ScheduledFuture newFuture = scheduleTask(job);  
        scheduledTasks.put(job.getId(), newFuture);  
    }  
}

修改接口,增加事件

@GetMapping("upd")  
public String upd(@RequestParam("cron") String cron) {  
    jobService.updateById(new TestJob(1, cron));  
    eventPublisher.publishEvent(new ScheduleTaskUpdateEvent(this, 1));  
    System.out.println("修改时间:"+ LocalDateTime.now());  
    return "success";  
}

结果

图片图片

可以看到,在收到修改任务的事件后,直接删除了原来的定时任务,创建了一个新的执行任务,即时生效,不需要等待一个执行周期就可立即执行。

小结

通过上述方法,我们可以在 Spring Boot 应用中实现动态计划任务,使得任务的执行更加灵活可控。

还实验了几种不同的方式,比如每秒轮询数据库、手动计算cron表达式 的执行时间。感觉就属这个事件驱动的方式最优雅。

来源:Java技术指北内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯