不重启项目,动态改变cron定时器的执行时间

之前在做oa项目的钉钉推送功能的时候,组长让我增加一个定时推送的功能,最好能实现用户在前端能自由修改时间。但是网上查了发现很多方法都需要重启项目才实现,后来从一篇博客上翻到说可以换一种思路,并不是去修改定时器时间,可以通过将之前的定时器停止注销,然后重新开启一个新的定时器来实现。

先上代码吧,基本上除了去掉了部分注入和方法参数的处理过程,整体逻辑都还是在的

/**
 * 钉钉消息推送定时器Controller
 *
 * @author zjs
 * @since 2019-03-21
 */
@Controller
@EnableScheduling
public class DingdingTimingController implements SchedulingConfigurer {
    private final static Logger logger = LoggerFactory.getLogger(DingdingTimingController.class);
    /**
     * (安装维护)信息
     */
    @Transactional(readOnly = false)
    public String timerOfOptRepair(DingdingTimer dingdingTimer) {
        try {
            //处理变量参数
            List result = dealResult();
            //处理文本整体
            String content = dealContent();
            dingdingService.sendMsg();
            logger.info("安装维护信息,钉钉定时推送成功);
            return Constants.SYSDIC_COMMON_SUCCESS;
        } catch (Exception e) {
            // TODO: handle exception
            logger.error("安装维护模块,钉钉定时推送异常", e);
            return Constants.SYSDIC_COMMON_DEFEAT;
        }
    }

    /**
     * (资质到期)信息
     */
    @Transactional(readOnly = false)
    public String timerOfOutTime(DingdingTimer dingdingTimer) {
        //如果有资质或者合同到期提醒的话,同时推送给钉钉
        try {
            //处理变量参数
            List result = dealResult();
            //处理文本整体
            String content = dealContent();
            dingdingService.sendMsg();
            logger.info("资质合同到期提醒信息,钉钉定时推送成功);
            return Constants.SYSDIC_COMMON_SUCCESS;
        } catch (Exception e) {
            // TODO: handle exception
            logger.error("资质合同到期提醒模块,钉钉定时推送异常", e);
            return Constants.SYSDIC_COMMON_DEFEAT;
        }
    }

    //不同类型模板参数获取
    public List dealResult(String type){
        List result = new ArrayList();
        if(Constants.DM_OPTREPAIR.equals(type)){
            result.add();
        }else if(Constants.DM_OUT_TIME.equals(type)){
            result.add();
        }
        return result;
    }

    public String dealContent(DingdingTimer dingdingTimer, List result) {
        return content;
    }

    /**
     * 截取接收人方法
     */
    public String receiversGet(DingdingTimer dingdingTimer){
        //截取钉钉发送人,拼接list
        return userIdList;
    }

    /**
     * 执行定时任务.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //项目启动时生成定时器
        StartTask();
    }

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private ScheduledFuture future;

    private Map> futureMap = new HashMap>();

    @Bean
    public ThreadPoolTaskScheduler trPoolTaskScheduler(){
        return new ThreadPoolTaskScheduler();
    }
    /**
     * 1,定义一个方法实现定时任务的启动
     * 2,定义一个方法实现用于终止定时任务
     * 3,修改定时任务时间:changeCron
     */
    /**
     * 启动定时器
     * @return
     */
    @RequestMapping("StartTask")
    public String StartTask(){
        /**
         * task:定时任务要执行的方法
         * trigger:定时任务执行的时间
         */
        //动态定时器调用
        List dingdingTimers = dingdingTimerClient.findAll(new DingdingTimer());
        logger.info("定时器表中现在有" + dingdingTimers.size() + "条记录");
        if(dingdingTimers.size() > 0) {
            for (DingdingTimer dingdingTimer : dingdingTimers) {
                futureSet(dingdingTimer);
            }
        }
        return "startTask";
    }

    /**
     * 停止定时任务
     * @return
     */
    @RequestMapping("endTask")
    public String endTask(DingdingTimer dingdingTimer){
        logger.info("判断:" + (futureMap.size() > 0));
        logger.info("长度是:" + futureMap.size());
        if(futureMap.size() > 0){
            if(futureMap.containsKey(dingdingTimer.getId())){
                //map.keySet()返回的是所有key的值
                ScheduledFuture f = futureMap.get(dingdingTimer.getId());//得到每个key多对用value的值
                if(!f.isCancelled()){
                    f.cancel(true);
                    System.out.printf("Main:Canceled:%s\n",f.isCancelled());
                    System.out.printf("Main:Done:%s\n",f.isDone());
                }
            }
        }


        logger.info("endTask");
        return "endTask";
    }

    /**
     * 改变调度的时间
     * 步骤:
     * 1,先停止定时器
     * 2,再启动定时器
     */
    @RequestMapping("changeTask")
    public String changeTask(String id){
        //定义新的执行时间
        DingdingTimer dingdingTimer = dingdingTimerClient.findById(id);
        //停止定时器
        endTask(dingdingTimer);
        if(dingdingTimer != null) {
            futureSet(dingdingTimer);
        }
        //启动定时器
        //StartTest();
        logger.info("changeTask");
        return "changeTask";
    }

    public class myRunable implements Runnable{
        DingdingTimer dingdingTimer;
        public myRunable(DingdingTimer dingdingTimer) {
            this.dingdingTimer = dingdingTimer;
        }

        @Override
        public void run() {
            logger.info("定时任务要执行的方法"+new Date());
            String result = "";
            String type = "";
            if(Constants.DM_OPTREPAIR.equals(type)){
                result = timerOfOptRepair(dingdingTimer);
            }
            if(Constants.DM_OUT_TIME.equals(type)){
                result = timerOfOutTime(dingdingTimer);
            }
            //存入推送记录
            dingdingSendClient.save();
        }
    }

    public void futureSet(DingdingTimer dingdingTimer){
        logger.info(dingdingTimer.getTitle());
        logger.info(dingdingTimer.getFixedTime());
        future = threadPoolTaskScheduler.schedule(new myRunable(dingdingTimer), new CronTrigger(dingdingTimer.getFixedTime()));
        //用type当key,定时器当value,相同key会覆盖
        logger.info("key为:"+dingdingTimer.getId()+"的value改为"+future);
        futureMap.put(dingdingTimer.getId(),future);
    }
}
 
  

每当项目运行的时候,会首先查询数据库中的定时器表中有几条定时记录,再一次startTask,完成定时器的初始化,而再futureSet方法中,根据定时器id存入Map这样就可以记录当前共启动了哪几个定时器,当修改定时器时间时,调用changeTask方法,先endTask,在获取新的时间,运行新的startTask,并向Map中存入新的记录,由于Map存入相同的key,value会覆盖,即实现了动态修改定时器的需求。

注:这样的做法目前还没去研究过有什么弊端,欢迎大神来指出,有发现的情况是部署在服务器的时候有发生过执行过几次之后就不再执行的情况,情况未知,之后解决了的话会再更新,希望能给大家提供帮助。

你可能感兴趣的:(不重启项目,动态改变cron定时器的执行时间)