springboot实现动态定时任务

一、前言

时隔多月,文章不曾更新,年底了,还是想提笔写一下,虽然写的不好,文章也不曾进行深入的讲解,但是客官,懂得都懂啊,您说对吧?(挑眉),来来来,天冷了,我们温壶热茶,让我将本文细细道来,客官你慢品慢听可好?

本文讲述的是定时任务的升级版本——动态定时任务,定时任务的相关可参考这篇文章 java 以及 springboot 分别实现定时器

那么动态定时任务的优点在哪里呢?比如说,我们的项目需要新增一个定时任务或者需要删除一个定时任务,亦或者需要修改某一个定时任务的参数,那我们最常规的做法是什么?只能修改代码,然后替换服务器的代码,对吧?这样做费时费力还容易出错,这时,动态定时任务的优点就体现出来了

  1. 无需修改代码
  2. 无需停服替换代码
  3. 避免意外情况的发生

那么动态定时任务要怎么实现呢?客官且细细看来

二、创建线程池

// 创建ThreadPoolTaskScheduler线程池
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.initialize();
        //设置线程池容量
        executor.setPoolSize(5);
        //线程名前缀
        executor.setThreadNamePrefix("dynamicTask-");
        //当调度器shutdown被调用时等待当前被调度的任务完成
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //线程关闭的等待时长
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

ThreadPoolTaskScheduler,可以很方便的对重复执行的任务进行调度管理,相比于通过java自带的周期性任务线程池ScheduleThreadPoolExecutor,此bean对象支持根据cron表达式创建周期性任务

关于线程池的配置,上述代码中已有注释说明,这里就不再赘述

三、新建 DynamicTask 工具类

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;

/**
 * @Author: zp.wei
 * @DATE: 2020/11/30 11:40
 */
@Component
public class DynamicTask {

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    public Map<String, ScheduledFuture<?>> taskMap = new HashMap<>();


    /**
     * 添加任务
     *
     * @param name
     * @param cron
     * @return
     */
    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;
    }


    /**
     * 停止任务
     *
     * @param name
     * @return
     */
    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中写下如下代码,当然,我这里仅仅是为了测试哈。所以就把参数写在了代码里,测试结果就不贴了,不出意外的话,不会出问题,如果出了问题,我也绝对不会承认是我的问题(哼哼)

@GetMapping("dynamicTask")
    public Result dynamicTask() {
        String cron = "*/5 * * * * ?";
        new DynamicTask().add("A", cron);
        new DynamicTask().add("B", cron);
        return Result.ok();
    }

五、将参数写入数据库

上面仅仅是解决了动态更改定时任务的问题,可那不是我们每次增加一个定时任务都要去发一次请求,并且将相关参数传递过去吗?一旦某个任务的参数出了问题,就又得调用一次删除的接口,然后再一次新增,可要是记不得是哪一个定时任务出了问题,万一停错了任务,这个锅背在身上那是肯定甩不掉了,不要捉急,对于这个我们也是有解决办法的…那就是将每个任务的参数写进数据库,这样我们每一次新增任务都把相关信息网数据库存一下,一旦某个任务的参数写错了,我们可以通过查看相关参数进行更正,从而达到更正定时任务的目的。

那就肯定有人说,假如我有十几个定时任务,在我项目重启后,我岂不是要请求十几次接口吗?这是不是太麻烦了?有没有更好的处理方式?(嗯…灵魂三连问),对于这三个问题,我只能说,肯定有更好的处理方式呀,客官你不要捉急,热茶要细品,才能存齿留香,回味悠长…

六、初始化定时任务

上面说到将定时任务的参数写到了数据库,那么我们只需要在项目重启后,查询一遍数据库,然后循环一下启动就好了呀,我们在刚刚的工具类中我们新增以下代码

	/**
     * 初始化任务 在服务器重启后自动启动任务
     */
	@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 注解,表示这个东西我只需要在项目启动后执行一次(警告你你别瞎执行哈),这样,我们每次在项目重启以后,这段代码都会自动跑一次,读取数据库,按部就班的执行定时任务

七、结语

当然了,我这里只是提供一个简单的例子,更多的需要你们自己改一下哦,客官你看,这是不是就是你要的?哦,茶喝完了呀?这么冷的天,来来来,续上续上

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值