SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版

这篇具有很好参考价值的文章主要介绍了SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前段时间编写了一篇博客SpringBoot 动态操作定时任务(启动、停止、修改执行周期,该篇博客还是帮助了很多同学。
但是该篇博客中的方法有些不足的地方:

  1. 只能通过前端控制器controller手动注册任务。【具体的应该是我们提前配置好我们的任务,配置完成后让springboot应用帮我们加载并注册任务】
  2. 无法打印任务的启动时间、下次执行时间及任务耗时等情况。

所以针对以上的不足我对该方案进行了整改。

新方案涉及4个类:

  1. TaskSchedulerConfig 任务调度器及任务注册器的配置
  2. ScheduledTaskRegistrar 任务注册器,用于将定时任务注册到调度器中
  3. ScheduledTaskHolder 任务的包装类
  4. ITask 任务抽象类

提醒:该定时任务只能实现单机环境下使用,无法在分布式环境下使用。

一、核心实现如下:

1、配置类TaskSchedulerConfig

该配置类往spring容器中注册了两个bean,第一个bean为org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler,该bean为spring提供的基于线程池的任务调度器,其本质是持有一个java.util.concurrent.ScheduledThreadPoolExecutor,这里需要注意初始化ScheduledThreadPoolExecutor的时候最大线程数为Integer.MAX_VALU。
setRemoveOnCancelPolicy方法如果设置为true,则在中断当前执行的任务后会将其从任务等待队列(如果队列中有该任务)中移除。
第二个bean为ScheduledTaskRegistrar即我们的任务注册器,该bean需要持有一个任务调度器并且需要配置任务列表【目前任务列表需要手动配置如果同学们想增强的话可以自己实现注解扫描配置或者包扫描配置】。

package com.bbs.config.scheduled;

import com.bbs.task.MoniterTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.util.Arrays;

/**
 * @Author whh
 * @Date: 2023/03/15/ 20:15
 * @description
 */
@Configuration
public class TaskSchedulerConfig {


    /**
     *
     * 本质是ScheduledThreadPoolExecutor的包装,其中初始化ScheduledThreadPoolExecutor的构造函数如下
     * 特别的要注意最大线程数为 Integer.MAX_VALUE
     *
     *  public ScheduledThreadPoolExecutor(int corePoolSize,
     *                                        ThreadFactory threadFactory,
     *                                        RejectedExecutionHandler handler) {
     *         super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
     *               new DelayedWorkQueue(), threadFactory, handler);
     *     }
     * @return
     */
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(3);
        threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
        return threadPoolTaskScheduler;
    }

    @Bean
    public ScheduledTaskRegistrar taskRegistrar(ThreadPoolTaskScheduler scheduler){
        ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar(scheduler);
        MoniterTask moniterTask = new MoniterTask("*/30 * * * * ?");
        moniterTask.setTaskName("监控任务");
        moniterTask.setTaskDescription("每隔30s对机器进行监控");
        taskRegistrar.setTaskes(Arrays.asList(moniterTask));
        return taskRegistrar;
    }
}

2、任务注册器ScheduledTaskRegistrar

该任务注册器实现了org.springframework.beans.factory.InitializingBean接口,所以可以达到配置完成后让springboot应用帮我们加载并注册任务。该bean主要有5个方法,包括注册任务、查询所有任务、立即执行任务、暂停任务、任务恢复。其中立即执行任务并未使用调度器的线程执行而是使用用户线程执行

package com.bbs.config.scheduled;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * @Author whh
 * @Date: 2023/03/15/ 19:44
 * @description 任务注册
 */
public class ScheduledTaskRegistrar implements InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskRegistrar.class);

    /**
     * 任务调度器
     */
    private ThreadPoolTaskScheduler scheduler;


    /**
     * 任务列表
     */
    private List<ITask> taskes;

    private final Map<String, ScheduledTaskHolder> register = new ConcurrentHashMap<>();


    public ScheduledTaskRegistrar(ThreadPoolTaskScheduler scheduler) {
        this.scheduler = scheduler;
    }


    /**
     * 注册任务
     */
    public void register() {
        for (ITask task : taskes) {
            ScheduledFuture<?> future = this.scheduler.schedule(task, new CronTrigger(task.getCron()));
            ScheduledTaskHolder holder = new ScheduledTaskHolder();
            holder.setScheduledFuture(future);
            holder.setTask(task);
            register.put(task.getClass().getName(), holder);
        }

    }


    /**
     * 查询所有任务
     *
     * @return
     */
    public Collection<ScheduledTaskHolder> list() {
        return register.values();
    }


    /**
     * 立即执行任务
     *
     * @param className
     */
    public void start(String className) {
        ScheduledTaskHolder holder = register.get(className);
        if (holder != null) {
            holder.getTask().run();
        }
    }


    /**
     * 暂停任务
     *
     * @param className
     */
    public void pause(String className) {
        ScheduledTaskHolder holder = register.get(className);
        if (holder != null) {
        if(holder.terminate()){
                return;
            }
            ScheduledFuture<?> future = holder.getScheduledFuture();
            future.cancel(true);
        }
    }


    /**
     *重启任务
     * @param className
     * @param cron
     */
    public void restart(String className,String cron){
        ScheduledTaskHolder holder = register.get(className);
        if (holder != null) {
         if(!holder.terminate()){
               //暂停原任务
            holder.getScheduledFuture().cancel(true);
            }
            ITask task = holder.getTask();
            if(!StringUtils.isEmpty(cron)){
               task.setCron(cron);
            }
            ScheduledFuture<?> future = this.scheduler.schedule(task, new CronTrigger(task.getCron()));
            holder.setScheduledFuture(future);

        }
    }

    public void setTaskes(List<ITask> taskes) {
        this.taskes = taskes;
    }

    private void log(){
        register.forEach((k,v)->{
            LOGGER.info("register {} complete,cron {}",k,v.getTask().getCron());

        });

    }

    @Override
    public void afterPropertiesSet(){
        register();
        log();
    }
}

3、任务包装类ScheduledTaskHolder

任务包装类包含具体任务的实例、任务执行的ScheduledFuture以及任务的运行状态。

package com.bbs.config.scheduled;

import java.util.concurrent.ScheduledFuture;

/**
 * @Author whh
 * @Date: 2023/03/15/ 19:45
 * @description
 */
public class ScheduledTaskHolder {

    /**
     * 具体任务
     */
    private ITask task;
    /**
     *result of scheduling
     */
    private ScheduledFuture<?> scheduledFuture;


    public ITask getTask() {
        return task;
    }

    public void setTask(ITask task) {
        this.task = task;
    }

    public ScheduledFuture<?> getScheduledFuture() {
        return scheduledFuture;
    }

    public void setScheduledFuture(ScheduledFuture<?> scheduledFuture) {
        this.scheduledFuture = scheduledFuture;
    }

    public boolean terminate() {
        return scheduledFuture.isCancelled();
    }
}
4、 任务抽象类ITask

任务抽象类ITask实现了Runnable接口同时提供了抽象方法execute用于自定义任务实现,同时对任务进行了增强,增加了任务执行信息的打印。

package com.bbs.config.scheduled;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @Author whh
 * @Date: 2023/03/15/ 19:46
 * @description
 * 任务的抽象类
 * 用于做切面打印任务执行的时间,耗时等信息
 */
public abstract class ITask implements Runnable{


    private static final Logger LOGGER = LoggerFactory.getLogger("ITask");
    private String cron;
    private String taskName;
    private String taskDescription;

    public ITask(String cron) {
        this.cron = cron;
    }

    @Override
    public void run() {
        LocalDateTime start = LocalDateTime.now();
        execute();
        LocalDateTime end = LocalDateTime.now();
        Duration duration = Duration.between(start, end);
        long millis = duration.toMillis();
        Date date = new CronTrigger(this.cron).nextExecutionTime(new SimpleTriggerContext());
        LOGGER.info("任务:[{}]执行完毕,开始时间:{},结束时间:{},耗时:{}ms,下次执行时间{}",this.taskName, start,end,millis,date);
    }


	/**
     * 用户的任务实现
     */
    public abstract void execute();


    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getTaskDescription() {
        return taskDescription;
    }

    public void setTaskDescription(String taskDescription) {
        this.taskDescription = taskDescription;
    }
}

二、前端控制器TaskController

package com.bbs.task.controller;

import com.bbs.config.scheduled.ITask;
import com.bbs.config.scheduled.ScheduledTaskRegistrar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author whh
 * @Date: 2023/03/15/ 21:18
 * @description
 */
@RestController
@RequestMapping("/task")
public class TaskController {


    @Autowired
    private ScheduledTaskRegistrar scheduledTaskRegistrar;

    @GetMapping
    public List<Map<String,Object>> listTask(){
        return scheduledTaskRegistrar.list().stream().map(e->{
            Map<String,Object> temp = new HashMap<>();
            ITask task = e.getTask();
            temp.put("任务名",task.getTaskName());
            temp.put("任务描述",task.getTaskDescription());
            temp.put("cron表达式",task.getCron());
            temp.put("任务状态",e.terminate()?"已停止":"运行中");
            temp.put("任务类名",task.getClass().getName());
            temp.put("下次执行时间",new CronTrigger(task.getCron()).nextExecutionTime(new SimpleTriggerContext()));
            return temp;
        }).collect(Collectors.toList());
    }



    @PostMapping("/pause")
    public void pause(String className){
        scheduledTaskRegistrar.pause(className);
    }


    @PostMapping("/start")
    public void start(String className){
        scheduledTaskRegistrar.start(className);
    }

    @PostMapping("/restart")
    public void restart(String className,String cron){
        scheduledTaskRegistrar.restart(className,cron);
    }
}

三、自定义任务实现

package com.bbs.task;

import com.bbs.config.scheduled.ITask;

/**
 * @Author whh
 * @Date: 2023/03/15/ 21:16
 * @description
 */
public class MonitorTask extends ITask {

    public MonitorTask(String cron) {
        super(cron);
    }

    @Override
    public void execute() {
        System.out.println("hello world ....");
    }
}

四、测试

1、应用启动时控制台会打印我们注册的任务信息
SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版,springboot,spring boot,java,后端
2、任务执行情况
SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版,springboot,spring boot,java,后端
3、前端控制器查询任务
SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版,springboot,spring boot,java,后端
调用暂停API后任务停止执行,重启任务后任务又重新开始执行。
SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版,springboot,spring boot,java,后端文章来源地址https://www.toymoban.com/news/detail-640836.html

到了这里,关于SpringBoot 动态操作定时任务(启动、停止、修改执行周期)增强版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot实现固定、动态定时任务 | 三种实现方式

    阅读完本文:🐱‍👓 知晓 SpringBoot 用注解如何实现定时任务 明白 SpringBoot 如何实现一个动态定时任务 (与数据库相关联实现) 理解 SpringBoot 实现设置时间执行定时任务 (使用 ThreadPoolTaskScheduler 实现) 用注解实现是真的简单,只要会 cron 表达式就行。🧙‍♂️ 第一步 :

    2024年02月16日
    浏览(41)
  • Spring Boot集成Quartz实现定时任务的动态创建、启动、暂停、恢复、删除

    一、整个 Quartz 的代码流程基本基本如下: 首先需要创建我们的任务(Job),比如取消订单、定时发送短信邮件之类的,这是我们的任务主体,也是写业务逻辑的地方。 创建任务调度器(Scheduler),这是用来调度任务的,主要用于启动、停止、暂停、恢复等操作,也就是那几个api的

    2024年02月11日
    浏览(42)
  • Linux 系统中查看和停止删除定时任务

    在Linux或类Unix系统中,通常使用crontab命令在指定的时间执行一个shell脚本或者一系列Linux命令,也就是通常所说的定时任务。 crond crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此

    2024年02月09日
    浏览(36)
  • Spring Boot进阶(69):轻松实现定时任务持久化!SpringBoot集成quartz带你玩转定时任务删除、暂停、获取等操作!【附项目源码】

            现如今,随着市场竞争加剧,各个企业都在不断寻求提高效率、降低成本的方法,此时使用自动化工具已成为必不可少的选择。而在众多的自动化工具中,定时任务已经成为一项必备工具,而Quartz就是一个非常好用的定时任务框架,它的轻量级、高可靠性、易于使

    2024年02月09日
    浏览(60)
  • xxl-job定时任务配置应用以及添加到自己已有的springboot项目中实现动态API调用

    XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 本篇文章主要是对xuxueli的xxl-job做一个简单的配置,以及将其添加到自己已有的项目中进行api调用。 一、xxl-job安装 1、首先

    2024年02月03日
    浏览(40)
  • 修改群晖套件的启动、停止命令

    环境:DSM6.2 问题:群晖套件起不来,想修改套件启动命令 解决办法:修改套件配置文件 背景:1.矿神套件zerotier,不能自动添加路由,有时候zerotier只能跟自己通讯 2.矿神cloudflared套件,默认使用qui2协议启动,且不能指定ipv6协议 解决过程:修改启动配置文件 群晖套件的目录

    2024年02月08日
    浏览(40)
  • (18)不重启服务动态停止、启动RabbitMQ消费者

            我们在消费RabbitMQ消息的过程中,有时候可能会想先暂停消费一段时间,然后过段时间再启动消费者,这个需求怎么实现呢?我们可以借助RabbitListenerEndpointRegistry这个类来实现,它的全类名是org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry,通过这个类可以

    2024年02月09日
    浏览(36)
  • springboot集成kafka消费手动启动停止

    在月结,或者某些时候,我们需要停掉kafka所有的消费端,让其暂时停止消费,而后等月结完成,再从新对消费监听恢复,进行消费,此动作不需要重启服务,最后源码下载 1.通过定时任务自动触发,通过@Scheduled,在某个时间点暂停kafka某个监听的消费,也可以在某个时间点

    2024年02月06日
    浏览(45)
  • springboot定时任务:同时使用定时任务和websocket报错

    项目使用了websocket,实现了消息的实时推送。后来项目需要一个定时任务,使用org.springframework.scheduling.annotation的@EnableScheduling注解来实现,启动项目之后报错 打断点 进入代码发现是这个定时任务的bean为null 由于先写的websocket推送消息,运行正常。之前一个项目只有一个定时任

    2024年02月11日
    浏览(38)
  • springboot 与异步任务,定时任务,邮件任务

    在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题。 SpringBoot 实现比较简单 主启

    2024年02月10日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包