基于Spring实现零重启自由编排任务的定时管理器

 更新时间:2023年07月25日 14:03:46   作者:我是小趴菜  
我们发现,我们使用Spring自带的定时任务如果要有修改,那么就要修改代码,然后重启项目,所以本文就带大家实现一个零重启自由编排任务的定时管理器吧

大家好,我是小趴菜,关于定时任务相信大家在项目中使用了很多,我们一般都是使用Spring自带的@EnableScheduling来实现定时任务

虽然Spring自带的定时任务已经可以满足我们的业务需求,但是它还是有不足的地方

  • 我们需要改变定时任务的时间,此时我们就需要重启项目
  • 我们不需要这个定时任务了,我们就要修改代码,然后重启项目
  • 后期我们又要开启这个定时任务,我们又要修改代码,然后重启项目

我们发现,我们使用Spring自带的定时任务如果要有修改,那么就要修改代码,然后重启项目,那么有没有办法能够让我们不重启项目就可以直接编排我们自己的定时任务呢?

答案是:有的,接下来我就带大家实现一个零重启自由编排任务的定时管理器

实现

创建一个普通的SpringBoot项目,首先我们需要一个配置类,来配置定时任务的线程池

package com.coco.schedule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
public class ScheduleConfig {
    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        //定制化我们的线程池
        taskScheduler.setPoolSize(1);        
        taskScheduler.setThreadNamePrefix("myTask-");
        return taskScheduler;
    }
}

创建一个接口,以供其它服务调用

/**
 * 定时任务的任务接口,需要实现Runnable接口
 */
public interface ScheduleTask extends Runnable {
    /**
     * 获取定时任务的名称
     */
    String getTaskName();
}

接下来就是定时任务的编排核心处理类

package com.coco.schedule;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
 * 定时任务管理器
 */
@Component
public class ScheduleManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleManager.class);
    @Resource
    private ThreadPoolTaskScheduler taskScheduler;
    /**
     * 内部正在执行的定时任务缓存
     */
    private Map<String,ScheduleTaskHolder> cache = new ConcurrentHashMap<>();
    /**
     * 启动一个定时任务
     * scheduleTask:定时任务实现类
     * cron:cron表达式
     */
    public String startTask(ScheduleTask scheduleTask,String cron) {
        ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(scheduleTask, new CronTrigger(cron));
        //使用UUID生成定时任务的唯一key
        String key = UUID.randomUUID().toString();
        //将定时任务与定时任务结果封装成ScheduleTaskHolder对象,
        //这个对象下文有源码,也是我们自定义的
        ScheduleTaskHolder scheduleTaskHolder = new ScheduleTaskHolder(scheduleTask,scheduledFuture);
        //将正在执行的定时任务缓存起来
        cache.put(key,scheduleTaskHolder);
        LOGGER.info("定时任务启动成功,key = {}",key);
        return key;
    }
    /**
     * 停止一个定时任务
     * @param key:缓存里面的定时任务的key
     */
    public void stopTask(String key) {
        //基本判断
        if(StrUtil.isBlank(key) || !cache.containsKey(key)) {
            return;
        }
        //从缓存中拿到这个定时任务
        ScheduleTaskHolder scheduleTaskHolder = cache.get(key);
        if(scheduleTaskHolder == null) {
            return;
        }
        ScheduledFuture scheduledFuture = scheduleTaskHolder.getScheduledFuture();
        //停止这个定时任务
        boolean isCancel = scheduledFuture.cancel(true);
        if(isCancel) {
            //停止成功,就从缓存中移除这个定时任务
            cache.remove(key);
            LOGGER.info("定时任务停止成功,key = {}",key);
        }else {
            LOGGER.error("定时任务停止失败,key = {}",key);
        }
    }
    /**
     * 停止一个定时任务
     * @param key:缓存里面的定时任务的key
     * @param cron:新的cron表达式
     */
    public String changeTask(String key,String cron) {
        //基本判空处理
        if(StrUtil.isBlank(key) || StrUtil.isBlank(cron)) {
            throw new RuntimeException("key and cron mast not null");
        }
        ScheduleTaskHolder scheduleTaskHolder = cache.get(key);
        if(scheduleTaskHolder == null) {
            throw new RuntimeException("ScheduleTaskHolder not exist,key = {}" + key);
        }
        //先停止这个定时任务
        stopTask(key);
        //然后重启开启一个定时任务
        return startTask(scheduleTaskHolder.getScheduleTask(),cron);
    }
}

定时任务与定时任务结果的缓存封装类

package com.coco.schedule;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
 * 定时任务管理器
 */
@Component
public class ScheduleManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleManager.class);
    @Resource
    private ThreadPoolTaskScheduler taskScheduler;
    /**
     * 内部正在执行的定时任务缓存
     */
    private Map<String,ScheduleTaskHolder> cache = new ConcurrentHashMap<>();
    /**
     * 启动一个定时任务
     * scheduleTask:定时任务实现类
     * cron:cron表达式
     */
    public String startTask(ScheduleTask scheduleTask,String cron) {
        ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(scheduleTask, new CronTrigger(cron));
        //使用UUID生成定时任务的唯一key
        String key = UUID.randomUUID().toString();
        //将定时任务与定时任务结果封装成ScheduleTaskHolder对象,
        //这个对象下文有源码,也是我们自定义的
        ScheduleTaskHolder scheduleTaskHolder = new ScheduleTaskHolder(scheduleTask,scheduledFuture);
        //将正在执行的定时任务缓存起来
        cache.put(key,scheduleTaskHolder);
        LOGGER.info("定时任务启动成功,key = {}",key);
        return key;
    }
    /**
     * 停止一个定时任务
     * @param key:缓存里面的定时任务的key
     */
    public void stopTask(String key) {
        //基本判断
        if(StrUtil.isBlank(key) || !cache.containsKey(key)) {
            return;
        }
        //从缓存中拿到这个定时任务
        ScheduleTaskHolder scheduleTaskHolder = cache.get(key);
        if(scheduleTaskHolder == null) {
            return;
        }
        ScheduledFuture scheduledFuture = scheduleTaskHolder.getScheduledFuture();
        //停止这个定时任务
        boolean isCancel = scheduledFuture.cancel(true);
        if(isCancel) {
            //停止成功,就从缓存中移除这个定时任务
            cache.remove(key);
            LOGGER.info("定时任务停止成功,key = {}",key);
        }else {
            LOGGER.error("定时任务停止失败,key = {}",key);
        }
    }
    /**
     * 停止一个定时任务
     * @param key:缓存里面的定时任务的key
     * @param cron:新的cron表达式
     */
    public String changeTask(String key,String cron) {
        //基本判空处理
        if(StrUtil.isBlank(key) || StrUtil.isBlank(cron)) {
            throw new RuntimeException("key and cron mast not null");
        }
        ScheduleTaskHolder scheduleTaskHolder = cache.get(key);
        if(scheduleTaskHolder == null) {
            throw new RuntimeException("ScheduleTaskHolder not exist,key = {}" + key);
        }
        //先停止这个定时任务
        stopTask(key);
        //然后重启开启一个定时任务
        return startTask(scheduleTaskHolder.getScheduleTask(),cron);
    }
}

测试

写好的程序怎么能不测试,首先我们需要创建一个自己的定时任务执行的业务处理类,我这里创建了个MyTask.class

package com.coco.schedule;
import org.springframework.stereotype.Component;
//需要实现ScheduleTask接口
@Component
public class MyTask implements ScheduleTask {
    @Override
    public String getTaskName() {
        return "MyTask";
    }
    @Override
    public void run() {
        //在这里就是我们定时任务的业务逻辑
        System.out.println("这是自定义的定时起任务");
    }
}

为了简单,我直接在启动类中编写接口了,大家别跟我一样这么懒哈

package com.coco;
import com.coco.schedule.MyTask;
import com.coco.schedule.ScheduleManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
    @Resource
    private ScheduleManager scheduleManager;
    @Resource
    private MyTask myTask;
    //启动我们的定时任务
    @GetMapping("/startTask")
    public String startTask() {
        String key = scheduleManager.startTask(myTask, "0/10 * * * * ? ");
        return key;
    }
    //修改我们的定时任务
    @GetMapping("/changeTask")
    public String changeTask(@RequestParam("key") String key) {
        String keyValue = scheduleManager.changeTask(key, "0/20 * * * * ? ");
        return keyValue;
    }
    //停止我们的定时任务
    @GetMapping("/stopTask")
    public String stopTask(@RequestParam("key") String key) {
        scheduleManager.stopTask(key);
        return "ok";
    }
}

可以先启动,然后修改,最后停止

到此这篇关于基于Spring实现零重启自由编排任务的定时管理器的文章就介绍到这了,更多相关Spring定时管理器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java Spring Boot 集成Zookeeper

    Java Spring Boot 集成Zookeeper

    这篇文章主要介绍了Java Spring Boot 集成Zookeeper,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • JAVA代码书写规范汇总详解

    JAVA代码书写规范汇总详解

    这篇文章主要介绍了JAVA代码书写规范汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 使用Spring自定义实现IOC和依赖注入(注解方式)

    使用Spring自定义实现IOC和依赖注入(注解方式)

    这篇文章主要介绍了使用Spring自定义实现IOC和依赖注入(注解方式),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java @Transactional与synchronized使用的问题

    Java @Transactional与synchronized使用的问题

    这篇文章主要介绍了Java @Transactional与synchronized使用的问题,了解内部原理是为了帮助我们做扩展,同时也是验证了一个人的学习能力,如果你想让自己的职业道路更上一层楼,这些底层的东西你是必须要会的
    2023-01-01
  • SpringBoot使用Graylog日志收集的实现示例

    SpringBoot使用Graylog日志收集的实现示例

    Graylog是一个生产级别的日志收集系统,集成Mongo和Elasticsearch进行日志收集,这篇文章主要介绍了SpringBoot使用Graylog日志收集的实现示例,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • SpringBoot实用小技巧之如何动态设置日志级别

    SpringBoot实用小技巧之如何动态设置日志级别

    这篇文章主要给大家介绍了关于SpringBoot实用小技巧之如何动态设置日志级别的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • 数据同步利器DataX简介及如何使用

    数据同步利器DataX简介及如何使用

    DataX 是阿里云 DataWorks数据集成 的开源版本,使用Java 语言编写,在阿里巴巴集团内被广泛使用的离线数据同步工具/平台,今天给大家分享一个阿里开源的数据同步工具DataX,在Github拥有14.8k的star,非常受欢迎
    2024-02-02
  • springboot中不能获取post请求参数的解决方法

    springboot中不能获取post请求参数的解决方法

    这篇文章主要介绍了springboot中不能获取post请求参数的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Java输出链表倒数第k个节点

    Java输出链表倒数第k个节点

    这篇文章主要介绍了Java输出链表倒数第k个节点的相关内容,涉及三种设计思路及代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • JAVA中的注解机制解读

    JAVA中的注解机制解读

    这篇文章主要介绍了JAVA中的注解机制解读,通过调用Java的反射机制相关API来访问annotation信息,首先加载使用注解的类,得到class类,然后再得到类相应的方法,成员变量,需要的朋友可以参考下
    2023-10-10

最新评论