前言
年底了,还是想提笔再写篇文章,虽然写的不好,文章也不曾进行深入的讲解,但是客官,懂得都懂啊,您说对吧?(挑眉),来来来,天冷了,我们温壶热茶,让我将本文细细道来,客官你慢品慢听可好?
本文讲述的是定时任务的升级版本——动态定时任务,定时任务的相关可参考这篇文章 java 以及 springboot 分别实现定时器
那么动态定时任务的优点在哪里呢?比如说,我们的项目需要新增一个定时任务或者需要删除一个定时任务,亦或者需要修改某一个定时任务的参数,那我们最常规的做法是什么?只能修改代码,然后替换服务器的代码,对吧?这样做费时费力还容易出错,这时,动态定时任务的优点就体现出来了
- 无需修改代码
- 无需停服替换代码
- 避免意外情况的发生
那么动态定时任务要怎么实现呢?客官且细细看来
创建线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Bean public ThreadPoolTaskScheduler threadPoolTaskScheduler() { ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); executor.initialize(); executor.setPoolSize(5); executor.setThreadNamePrefix("dynamicTask-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(60); return executor; }
|
ThreadPoolTaskScheduler,可以很方便的对重复执行的任务进行调度管理,相比于通过java自带的周期性任务线程池ScheduleThreadPoolExecutor,此bean对象支持根据cron表达式创建周期性任务
关于线程池的配置,上述代码中已有注释说明,这里就不再赘述
新建 DynamicTask 工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| mport org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import org.wzp.oauth2.util.DateUtil;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledFuture;
@Component public class DynamicTask {
@Autowired private ThreadPoolTaskScheduler threadPoolTaskScheduler;
public Map<String, ScheduledFuture<?>> taskMap = new HashMap<>();
public boolean add(String name, String cron) { if (null != taskMap.get(name)) { return false; } ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(getRunnable(name), new CronTrigger(cron)); taskMap.put(name, schedule); return true; }
public boolean stop(String name) { if (null == taskMap.get(name)) { return false; } ScheduledFuture<?> scheduledFuture = taskMap.get(name); scheduledFuture.cancel(true); taskMap.remove(name); return true; }
public Runnable getRunnable(String name) { return new Runnable() { @Override public void run() { System.out.println(name + "---动态定时任务运行---" + DateUtil.formatLocalDateTime()); } }; }
}
|
在这里,我只提供了两个方法,一个新增任务的方法,一个删除任务的方法。暂停任务和修改任务的方法也是存在的,当然了,这个可以有,只是我这里没有写出来,你们可以自己写一下
动态定时任务的使用
我们新增一个 taskController,在controller中写下如下代码,当然,我这里仅仅是为了测试哈。所以就把参数写在了代码里,测试结果就不贴了,不出意外的话,不会出问题,如果出了问题,我也绝对不会承认是我的问题(哼哼)
1 2 3 4 5 6 7
| @GetMapping("dynamicTask") public Result dynamicTask() { String cron = "*/5 * * * * ?"; new DynamicTask().add("A", cron); new DynamicTask().add("B", cron); return Result.ok(); }
|
将参数写入数据库
上面仅仅是解决了动态更改定时任务的问题,可那不是我们每次增加一个定时任务都要去发一次请求,并且将相关参数传递过去吗?一旦某个任务的参数出了问题,就又得调用一次删除的接口,然后再一次新增,可要是记不得是哪一个定时任务出了问题,万一停错了任务,这个锅背在身上那是肯定甩不掉了,不要捉急,对于这个我们也是有解决办法的……那就是将每个任务的参数写进数据库,这样我们每一次新增任务都把相关信息网数据库存一下,一旦某个任务的参数写错了,我们可以通过查看相关参数进行更正,从而达到更正定时任务的目的。
那就肯定有人说,假如我有十几个定时任务,在我项目重启后,我岂不是要请求十几次接口吗?这是不是太麻烦了?有没有更好的处理方式?(嗯……灵魂三连问),对于这三个问题,我只能说,肯定有更好的处理方式呀,客官你不要捉急,热茶要细品,才能存齿留香,回味悠长……
初始化定时任务
上面说到将定时任务的参数写到了数据库,那么我们只需要在项目重启后,查询一遍数据库,然后循环一下启动就好了呀,我们在刚刚的工具类中我们新增以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@PostConstruct public void initDynamic() { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); String cron = "*/5 * * * * ?"; list.forEach(s -> { ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(getRunnable(s), new CronTrigger(cron)); taskMap.put(s, schedule); }); }
|
这里我就不写查询数据库的办法了,直接将数据写在代码里,原理是一样的,然后我们添加一个 @PostConstruct 注解,表示这个东西我只需要在项目启动后执行一次(警告你你别瞎执行哈),这样,我们每次在项目重启以后,这段代码都会自动跑一次,读取数据库,按部就班的执行定时任务
结语
当然了,我这里只是提供一个简单的例子,更多的需要你们自己改一下哦,客官你看,这是不是就是你要的?哦,茶喝完了呀?这么冷的天,来来来,续上续上