SpringBoot定时任务动态扩展ScheduledTaskRegistrar详解
作者:昵称为空C 发布时间:2023-04-23 08:44:06
标签:SpringBoot,ScheduledTaskRegistrar,定时任务,动态扩展
摘要
本文主要介绍基于SpringBoot
定时任务ScheduledTaskRegistrar
的动态扩展,实现定时任务的动态新增和删除。
ScheduledTaskRegistrar类简要描述
平常使用方式配置
Application
启动类上添加注解@EnableScheduling
@EnableScheduling
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在需要定时的方法上添加定时注解
@Scheduled(cron = "0/10 * * * * ?")
@Slf4j
@Component
public class OtherScheduler {
@Scheduled(cron = "0/10 * * * * ?")
public void print(){
log.info("每10S打印一次");
}
@Scheduled(cron = "0/5 * * * * ?")
public void print5(){
log.info("每5S打印一次");
}
}
原理分析
默认的方式启动把ScheduledAnnotationBeanPostProcessor
该类实例化到SpringBoot
的Bean
管理中,并且该类持有一个ScheduledTaskRegistrar
属性,然后扫描出来拥有@Scheduled
注解的方法,添加到定时任务中。
添加定时任务到列表中
扫描到@Scheduled
注解的时候调用了该方法添加任务
public void addCronTask(Runnable task, String expression) {
if (!CRON_DISABLED.equals(expression)) {
addCronTask(new CronTask(task, expression));
}
}
启动定时任务
在对象实例化完成后,调用了afterPropertiesSet
方法,该方法实际使用中执行了
public void afterPropertiesSet() {
scheduleTasks();
}
protected void scheduleTasks() {
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
if (this.triggerTasks != null) {
for (TriggerTask task : this.triggerTasks) {
addScheduledTask(scheduleTriggerTask(task));
}
}
if (this.cronTasks != null) {
for (CronTask task : this.cronTasks) {
addScheduledTask(scheduleCronTask(task));
}
}
if (this.fixedRateTasks != null) {
for (IntervalTask task : this.fixedRateTasks) {
addScheduledTask(scheduleFixedRateTask(task));
}
}
if (this.fixedDelayTasks != null) {
for (IntervalTask task : this.fixedDelayTasks) {
addScheduledTask(scheduleFixedDelayTask(task));
}
}
}
private void addScheduledTask(@Nullable ScheduledTask task) {
if (task != null) {
this.scheduledTasks.add(task);
}
}
// 启动任务核心方法
public ScheduledTask scheduleCronTask(CronTask task) {
ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
boolean newTask = false;
if (scheduledTask == null) {
scheduledTask = new ScheduledTask(task);
newTask = true;
}
if (this.taskScheduler != null) {
scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
}
else {
addCronTask(task);
this.unresolvedTasks.put(task, scheduledTask);
}
return (newTask ? scheduledTask : null);
}
DynamicScheduledTaskRegistrar 动态任务注册类
下面改动主要涉及到线程池数量、新增任务、删除任务、销毁任务四个方面;
public class DynamicScheduledTaskRegistrar extends ScheduledTaskRegistrar {
private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskRegistrar.class);
private final Map<String,ScheduledTask> scheduledTaskMap = new LinkedHashMap<>(16);
public DynamicScheduledTaskRegistrar(){
super();
// 两种实现方案
//ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//TaskScheduler taskScheduler = new ConcurrentTaskScheduler(scheduledExecutorService);
// 第二种实现方案
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(8);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("dynamic-scheduled-task-");
taskScheduler.initialize();
this.setScheduler(taskScheduler);
}
/**
* 新增任务
* @param taskName
* @param cron
* @param runnable
*/
public Boolean addCronTask(String taskName,String cron,Runnable runnable){
if(scheduledTaskMap.containsKey(taskName)){
log.error("定时任务["+ taskName+"]已存在,添加失败");
return Boolean.FALSE;
}
CronTask cronTask = new CronTask(runnable,cron);
ScheduledTask scheduledTask = this.scheduleCronTask(cronTask);
scheduledTaskMap.put(taskName,scheduledTask);
log.info("定时任务["+taskName+"]新增成功");
return Boolean.TRUE;
}
/**
* 删除任务
* @param taskName
*/
public void cancelCronTask(String taskName){
ScheduledTask scheduledTask = scheduledTaskMap.get(taskName);
if(null != scheduledTask){
scheduledTask.cancel();
scheduledTaskMap.remove(taskName);
}
log.info("定时任务["+taskName+"]删除成功");
}
@Override
public void destroy() {
super.destroy();
scheduledTaskMap.values().forEach(ScheduledTask::cancel);
}
}
线程池数量问题
由于默认是单线程的,如果任务阻塞时间过长则会导致后续的任务阻塞,所以尽量是异步任务或者是线程池数量大一点,则可以避免这个问题
DynamicScheduledTaskService
@Service
public class DynamicScheduledTaskService {
private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskService.class);
private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar();
/**
* 新增任务
* @param taskName
* @param cron
*/
public void add(String taskName,String cron){
Boolean result = dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> print(taskName));
log.info("定时任务添加结果:" + result);
}
/**
* 取消任务
* @param taskName
*/
public void cancel(String taskName){
dynamicScheduledTaskRegistrar.cancelCronTask(taskName);
}
private void print(String taskName){
log.info(taskName+"开始");
try{
Thread.sleep(9000L);
log.info(taskName+"结束111");
}catch (Exception ex){
}
log.info(taskName+"结束");
}
}
SchedulerController
@RestController
@RequestMapping(value = "scheduler")
public class SchedulerController {
@Autowired
private DynamicScheduledTaskService dynamicScheduledTaskService;
@GetMapping(value = "add")
public Object add(String taskName,String cron){
dynamicScheduledTaskService.add(taskName,cron);
return "SUCCESS";
}
@GetMapping(value = "cancel")
public Object cancel(String jobName){
dynamicScheduledTaskService.cancel(jobName);
return "SUCCESS";
}
}
测试结果
新增的任务都睡眠了9S
新增调度任务
删除调度任务
来源:https://juejin.cn/post/7187628561430216765
0
投稿
猜你喜欢
- 本文实例为大家分享了C#超市收银系统设计的具体代码,供大家参考,具体内容如下1.登录界面代码如下:using System;using Sy
- Solr我还是个菜鸟,写这一些文章只是记录一下最近一段时间学习Solr的心得。 Solr是什么? 最近我学Solr的时候,一直看到一句话,S
- 一、分步骤集成1.1 整合连接池hikariCP介绍:HikariCP 是一个高性能的 JDBC 连接池组件,可以避免连接频繁建立、关闭的开
- 这篇文章主要介绍了Springboot2.0处理自定义异常并返回json,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 如下所示:# ===============================================================
- 上篇文章老王买产品 我们从最原始的基本实现方法,到简单(静态)工厂,然后使用工厂方法设计模式进行改造,最后考虑产品会产生变体,我们
- 本博文将为您提供自Java 7以来增加的很棒的新功能的示例。我将展示每个Java版本的至少一项重大改进,一直到2020年秋季发布的Java
- 本文介绍了Spring Security 控制授权的方法,分享给大家,具体如下:使用授权方法进行授权配置每一个 Spring Securit
- nacos升级spring cloud 2020.0无法使用bootstrap.yml之前用spring cloud整合nacos,需要一个
- Java 使用IO流实现大文件的分割与合并文件分割应该算一个比较实用的功能,举例子说明吧比如说:你有一个3G的文件要从一台电脑Copy到另一
- java Hibernate多对多映射前言:一、单向多对多 单向多对多的例子用人和职位来举例,一个人可以有多个职位
- 懒加载---就是我们在spring容器启动的是先不把所有的bean都加载到spring的容器中去,而是在当需要用的时候,才把这个对象实例化到
- 这篇文章主要介绍了spring如何实现两个xml配置文件间的互调,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 1.简介使用线程池可以避免线程的频繁创建以及销毁。JAVA中提供的用于实现线程池的API:Executor、ExecutorService、
- 本文实例为大家分享了iOS新浪微博分享功能的具体代码,供大家参考,具体内容如下做新浪分享 需先去http://open.weibo.com/
- 多数据源的目的在于一个代码模块可调用多个数据库的数据进行某些业务操作。MyBatis-Plus开发者写了一个多数据源叫dynamic-dat
- 报错翻译: compileSdkVersion android-24”需要JDK 1.8或更高版本编译。报错现象如下图:原因:st
- MongoDB是介于关系数据库和非关系数据库之间的一种产品,文件的存储格式为BSON(一种JSON的扩展),这里就主要介绍Java通过使用m
- public static String getCharset(File file) { &n
- 一、获取接口请求的数据可以在Interceptor的afterCompletion中实现但是要重写RequestWrapper代码记录如下: