首页 > 其他分享 >Springboot中动态管理定时任务

Springboot中动态管理定时任务

时间:2024-09-25 10:34:32浏览次数:8  
标签:Springboot cron 任务 taskId import 定时 动态 public

引言

基于cron表达式的定时任务实现,因为cron表达式对于每个任务不确定,所以使用线程池来动态的创建和销毁定时任务

依赖

因为使用的spring自带的调度功能,所以没有额外的依赖,我的项目版本为:Static Badge Static Badge

使用

首先需要定义一个线程池,使用@configuration 注解配置

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.scheduling.TaskScheduler;  
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

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

这个配置中定义了一个调度的线程池,并且配置了线程池大小、线程名称前缀以及初始化操作

然后实现一个定时管理

import com.google.common.collect.Maps;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.scheduling.TaskScheduler;  
import org.springframework.scheduling.support.CronTrigger;  
import org.springframework.stereotype.Service;  
  
import javax.annotation.Resource;  
import java.time.LocalDateTime;  
import java.time.format.DateTimeFormatter;  
import java.util.Map;  
import java.util.concurrent.ScheduledFuture;

@Slf4j
@Service
public class DynamicScheduledTaskService {

    @Resource
    private TaskScheduler taskScheduler;
    
    /**  
     * 使用Map来关联任务ID和ScheduledFuture
     * 如有必要,可以通过Redis等数据库进行管理
     */
    private final Map<String, ScheduledFuture<?>> tasks = Maps.newConcurrentMap();

	/**
	 * 周期调度执行任务
	 *  
	 * @param taskId         任务ID  
	 * @param cronExpression cron表达式  
	 * @param task           实际任务  
	 */
    public void schedulingTask(String taskId, String cronExpression, Runnable task) {  
        log.info("添加定时调度任务:{},cron为:{}", taskId, cronExpression);  
  
        // 取消已存在的同ID任务
        ScheduledFuture<?> existingTask = tasks.get(taskId);
        if (existingTask != null && !existingTask.isCancelled()) {  
            existingTask.cancel(false);  
        }  
  
        // 包装任务以便在执行完毕后自动取消  
        Runnable wrappedTask = () -> {
            log.info("{} 执行定时调度任务:{}", DateUtil.nowToString(), taskId);  
            task.run();  
        };  
        // 安排任务并保存其Future  
        ScheduledFuture<?> future = taskScheduler.schedule(wrappedTask, new CronTrigger(cronExpression));  
        tasks.put(taskId, future);  
    }  
	    
	/**
	 * 定时单次执行调度任务
	 *  
	 * @param taskId   任务ID
	 * @param execTime 执行时间
	 * @param task     实际任务
	 */
	public void singleScheduleTask(String taskId, LocalDateTime execTime, Runnable task) {
	    log.info("添加定时调度任务:{},执行时间为:{}", taskId, execTime);
	  
	    DateTimeFormatter cronTimeFormatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ?");
	    // 取消已存在的同ID任务
	    ScheduledFuture<?> existingTask = tasks.get(taskId);
	    if (existingTask != null && !existingTask.isCancelled()) {
	        existingTask.cancel(false);
	    }
	  
	    // 包装任务以便在执行完毕后自动取消
	    Runnable wrappedTask = () -> {
	        log.info("{} 执行单次调度任务:{}", LocalDateTime.now(), taskId);
	        try {
	            task.run();
	        } finally {  
	            // 无论任务成功还是异常终止,都取消后续执行  
	            this.stopTask(taskId);  
	        }  
	    };  
	    // 安排任务并保存其Future  
	    ScheduledFuture<?> future = taskScheduler.schedule(wrappedTask, new CronTrigger(cronTimeFormatter.format(execTime)));  
	    tasks.put(taskId, future);
	}
    public void stopTask(String taskId) {  
        ScheduledFuture<?> future = tasks.get(taskId);  
        if (future != null && !future.isCancelled()) {  
            future.cancel(false);  
            tasks.remove(taskId);  
            log.info("{} 停止调度任务:{}", DateUtil.nowToString(), taskId);  
        }  
    }  
  
}


此定时管理服务中一共实现了两种情况

  1. 周期性的执行任务,手动取消才进行取消
  2. 单次执行任务,执行后自动销毁任务

使用服务样例

@Slf4j
@Service
public class UseScheduleService{

    @Resource
    private DynamicScheduledTaskService scheduledTaskService;

    public void startSchedulingTask(){
        // 其余逻辑
        String taskId="taskId";
        // cron表达式,以从0分钟开始,每隔一分钟执行一次为例
        String crontab = "0 0/1 * * * ?";

        scheduledTaskService.schedulingTask(taskId, crontab, () -> {
            // 此处是执行任务的逻辑
        });
    
    }

    
    public void startScheduleTask(){
        // 其余逻辑
        String taskId="taskId";
        // 执行的时间
        LocalDateTime execTime = LocalDateTime.now();

        scheduledTaskService.scheduleTask(taskId, execTime, () -> {
            // 此处是执行任务的逻辑
        });
    }

    public void stopTask(String taskId){
        scheduledTaskService.stopTask(taskId);
    }

}

这样就完成了定时任务的动态管理

定时管理之外

初始化

在使用定时任务的场景中,我们一般还会有重启服务时候,需要针对重启之前已经在执行的任务进行恢复定时,这里我选择使用Spring的 ApplicationRunner 进行服务启动后执行逻辑的管理

import lombok.extern.slf4j.Slf4j;  
import org.springframework.stereotype.Service;
import org.springframework.boot.ApplicationRunner;  
import org.springframework.boot.ApplicationArguments;
  
@Slf4j  
@Service
public class UseScheduleService implements ApplicationRunner{
@Resource
private DynamicScheduledTaskService dynamicScheduledTaskService;

    @Override  
    public void run(ApplicationArguments args) {
        log.info("============= 初始化定时任务begin =============");
        // 获取需要启动的列表
        List<Object> needStartList = new ArrayList<>();
        // 进行定时任务的写入
        needStartList.forEach(i->dynamicScheduledTaskService.startSchedulingTask(i.getTaskId(), i.getCronTab(),
        () -> {
        // 执行的逻辑
        }));
        log.info("============= 初始化定时任务end =============");
    }
}

定时任务工具类

我在使用定时任务时,还遇到了需要判定是否是合规的cron表达式以及根据cron表达式获取下次执行时间的需求,封装的工具类如下

import org.springframework.scheduling.support.CronSequenceGenerator;  
  
import java.time.LocalDateTime;  
import java.time.ZoneId;  
import java.util.Date;
  
public class CronUtils {  
  
    private CronUtils() {  
    }  
  
    /**  
     * 判断cron表达式是否无效  
     *  
     * @param cron cron表达式  
     * @return 判定结果  
     */  
    public static Boolean isInvalidCron(String cron) {  
        return !isValidCron(cron);  
    }  
  
    /**  
     * 判断cron表达式是否有效  
     *  
     * @param cron cron表达式  
     * @return 判定结果  
     */  
    public static Boolean isValidCron(String cron) {  
        try {  
            new CronSequenceGenerator(cron);  
            return true;  
        } catch (IllegalArgumentException ex) {  
            return false;  
        }  
    }  
  
    /**  
     * 获取下次执行时间  
     *  
     * @param cron cron表达式  
     * @return 下次执行时间  
     */  
    public static LocalDateTime getNextExecTime(String cron) {
        // 因为CronSequenceGenerator.next接受的入参只有java.util.Date,而我又习惯使用LocalDateTime,因此加入此转换
        return new CronSequenceGenerator(cron).next(new Date()).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();  
    }  
  
}

标签:Springboot,cron,任务,taskId,import,定时,动态,public
From: https://www.cnblogs.com/xing-chen-d/p/18429090

相关文章

  • 基于SpringBoot和Vue的餐饮管理系统
      基于springboot+vue实现的餐饮管理系统 (源码+L文+ppt)4-078   第4章系统设计   4.1总体功能设计一般个人用户和管理者都需要登录才能进入餐饮管理系统,使用者登录时会在后台判断使用的权限类型,包括一般使用者和管理者,一般使用者只能对菜品信息提供查阅和......
  • 【JAVA开源】基于Vue和SpringBoot学科竞赛管理系统
    本文项目编号T047,文末自助获取源码\color{red}{T047,文末自助获取源码}......
  • pythn自动化之allure报告动态刷新并邮件发送测试结果数据
    我们在做自动话测试时,一般情况下我们需要查看allure报告,需要allure启动一个服务,但是allure报告有个缺点就是报告不会自动刷新,你你能看到的页面报告就是第一次启动打开的报告,如果想要看到最新的服务报告,需要重新启动一下服务那么怎么样才能去看到我们想要的报告动态刷新呢?这里......
  • 基于springboot在线点餐系统
     基于springboot+vue实现的点餐系统 (源码+L文+ppt)4-077    第4章系统设计   4.1总体功能设计一般个人用户和管理者都需要登录才能进入点餐系统,使用者登录时会在后台判断使用的权限类型,包括一般使用者和管理者,一般使用者只能对美食信息提供查阅和个别使用信......
  • 【开题报告】基于Springboot+vue基于网上商品销售管理系统(程序+源码+论文) 计算机毕业
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,电子商务已成为现代商业活动的重要组成部分,深刻改变了人们的购物习惯和消费模式。网上商品销售管理系统作为电子商务平台的......
  • ArgoWorkflow教程(五)---Workflow 的多种触发模式:手动、定时任务与事件触发
    上一篇我们分析了argo-workflow中的archive,包括流水线GC、流水线归档、日志归档等功能。本篇主要分析Workflow中的几种触发方式,包括手动触发、定时触发、Event事件触发等。1.概述ArgoWorkflows的流水线有多种触发方式:手动触发:手动提交一个Workflow,就会触发一次构建......
  • 【笔记】Dynamic Taint Analysis 动态污点分析
    DynamicTaintAnalysis动态污点分析什么是动态污点分析?为什么要搞动态污点分析?“污点”指的是什么?DTA中的“污点”指代的是不可信的输入,比如用户输入、网络请求、文件数据等。比方说,如果把程序看作一个城市,外部输入的数据就像来自外界的货物。有些货物可能携带危险物质(恶意输......
  • 如何动态更改pyqtgraph中的文本
    我正在尝试创建一个继承pyqthraph的TextItem类的自定义类,以便我可以拥有多行,每行具有不同的颜色,就像仅1个TextItem对象一样。我希望能够在类中存储行位置和颜色的逻辑,基本上,如果删除文本行,所有后续行都会向上移动。下面的代码概要是:它是一种随机游走,绘......
  • 通信的基本概念以及串口和定时器使用
    一.数据传送的方式  串行通讯  速度慢,占用资源少,距离远  并行通讯  速度快,占用资源多二.通信方式  单工通讯    一个固定发送,一个固定接受  半双工通讯    对讲机  全双工通讯    电话三.数据同步方式  同步(有时钟......
  • [含文档+PPT+源码等]精品基于springboot实现的原生Andriod学生宿舍管理系统
    基于SpringBoot实现的原生Android学生宿舍服务系统的背景,可以从以下几个方面进行阐述:一、技术背景SpringBoot的优势:SpringBoot通过其自动配置、简化依赖管理、内嵌容器等特性,极大地简化了基于Spring框架的应用开发过程。这使得开发者能够快速搭建起稳定、可靠的后端服......