# 使用Quartz框架实现定时任务动态生成和执行

使用Quartz框架实现定时任务动态生成和执行


思考:需求场景

  • 从页面上动态添加接口Url,填写Corn表达式,约束Url接口的执行次数和时间。能够对任务进行管理(新增、执行、停止、删除)等功能。

  • Quartz的使用详情请看https://blog.csdn.net/qq_37248504/article/details/106874496


库表设计

t_task_cron:cron表达式表,每个任务的cron
DROP TABLE IF EXISTS `t_task_cron`;
CREATE TABLE `t_task_cron` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `corn` varchar(100) DEFAULT NULL,
  `cornname` varchar(100) DEFAULT NULL,
  `corecode` varchar(100) DEFAULT NULL,
  `recordertime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `t_task_cron` VALUES ('1', '*/10 * * * * ?', '每隔10s运行', 'EveryTenSeconds', '2020-12-22 20:34:56');
t_task_define:定义的要执行的任务

taskcode是唯一的,当前运行任务的code

DROP TABLE IF EXISTS `t_task_define`;
CREATE TABLE `t_task_define` (
  `id` int(100) NOT NULL AUTO_INCREMENT,
  `taskcode` varchar(255) NOT NULL,
  `taskname` varchar(255) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  `urlparams` varchar(255) DEFAULT NULL COMMENT 'url参数',
  `method` varchar(10) DEFAULT NULL,
  `cron` varchar(50) DEFAULT NULL,
  `definetime` datetime DEFAULT NULL,
  `recordername` varchar(100) DEFAULT NULL,
  `recordercode` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_taskcode` (`taskcode`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `t_task_define` VALUES ('1', 'test', 'task任务测试', 'localhost:8013/taskdefine/test1', null, 'post', '1', '2020-11-30 20:02:16', '1', '1');
t_task_deploy:已经部署的任务
DROP TABLE IF EXISTS `t_task_deploy`;
CREATE TABLE `t_task_deploy` (
  `id` varchar(50) NOT NULL,
  `taskid` varchar(50) DEFAULT NULL,
  `deployed` int(1) unsigned zerofill NOT NULL COMMENT '是否部署',
  `deploytime` datetime DEFAULT NULL,
  `recordername` varchar(255) DEFAULT NULL,
  `recordercode` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t_task_deploy` VALUES ('', '1', '1', '2020-12-22 20:35:43', null, null);
t_task_perform_log:任务执行日志状态
DROP TABLE IF EXISTS `t_task_perform_log`;
CREATE TABLE `t_task_perform_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `deployid` varchar(30) DEFAULT NULL,
  `status` int(255) DEFAULT NULL COMMENT '状态码',
  `url` varchar(150) DEFAULT NULL,
  `method` varchar(15) DEFAULT NULL COMMENT '请求方式',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

设计思路

页面提交相关操作,后端进行任务新建、执行、停止等功能。
在这里插入图片描述

类设计图

  • QuartzController:BaseController是我的工具Controller
    在这里插入图片描述
  • QuartzServiceImpl:具体业务逻辑实现类
    在这里插入图片描述
  • Job 执行的具体步骤
    在这里插入图片描述

QuartzUtils:

  • quartz任务启动、停止、重启、删除等操作的工具类
public class QuartzUtils {
    
    

    private final static Logger logger = LoggerFactory.getLogger(QuartzUtils.class);

    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     *
     * @param scheduler  调度器
     * @param quartzBean 定时任务信息类
     * @throws Exception
     */
    public static void createScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {
    
    
        try {
    
    
            //获取到定时任务的执行类  必须是类的绝对路径名称
            //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
            // 构建定时任务信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();
            // 设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            // 构建触发器trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (ClassNotFoundException e) {
    
    
            logger.error("定时任务类路径出错:请输入类的绝对路径");
        } catch (SchedulerException e) {
    
    
            logger.error("创建定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称暂停定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void pauseScheduleJob(Scheduler scheduler, String jobName) {
    
    
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
    
    
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
    
    
            logger.error("暂停定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称恢复定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void resumeScheduleJob(Scheduler scheduler, String jobName) {
    
    
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
    
    
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
    
    
            logger.error("启动定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称立即运行一次定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @param taskInfo
     * @throws SchedulerException
     */
    public static boolean runOnce(Scheduler scheduler, String jobName, TaskInfo taskInfo) {
    
    
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
    
    
            List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
            if(CollectionUtils.isEmpty(triggersOfJob)){
    
    
                boolean flag = false;
                for (Trigger trigger : triggersOfJob) {
    
    
                    if(jobKey.equals(trigger.getJobKey())){
    
    
                        flag = true;
                    }
                }
                // 当前任务没有Trigger则新建
                if(!flag){
    
    
                    // 定义QuartzBean
                    QuartzBean quartzBean = new QuartzBean();
                    // 定义任务执行的类
                    quartzBean.setJobClass("com.li.taskcenter.controller.quartz.MyTask");
                    // 定义任务的名称唯一
                    quartzBean.setJobName(taskInfo.getTaskcode());
                    // corn表达式
                    quartzBean.setCronExpression(taskInfo.getCorn());
                    // 创建任务执行执行
                    QuartzUtils.createScheduleJob(scheduler,quartzBean);
                    // 停止执行
                    QuartzUtils.pauseScheduleJob(scheduler,quartzBean.getJobName());
                }
            }
            // 执行一次
            logger.info("=====> 任务执行一次开始...");
            scheduler.triggerJob(jobKey);
            logger.info("=====> 任务执行一次结束...");
            return true;
        } catch (SchedulerException e) {
    
    
            logger.error("运行定时任务出错:" + e.getMessage());
            return false;
        }
    }

    /**
     * 更新定时任务
     *
     * @param scheduler  调度器
     * @param quartzBean 定时任务信息类
     * @throws SchedulerException
     */
    public static void updateScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {
    
    
        try {
    
    
            //获取到对应任务的触发器
            TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
            //设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            //重新构建任务的触发器trigger
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置对应的job
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
    
    
            logger.error("更新定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据定时任务名称从调度器当中删除定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobName) {
    
    
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
    
    
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
    
    
            logger.error("删除定时任务出错:" + e.getMessage());
        }
    }
}

MyTask

  • MyTask继承QuartzJobBean,重写executeInternal,实现具体的任务运行逻辑。
package com.li.taskcenter.controller.quartz;

import com.alibaba.fastjson.JSONObject;
import com.li.taskcenter.domain.taskcenter.TaskInfo;
import com.li.taskcenter.mapper.taskcenter.TaskDefineMapper;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Trigger;
import org.quartz.impl.JobExecutionContextImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Description:任务执行总类继承 QuartzJobBean重写 executeInternal()
 * @Author:LiDong
 * @Create:2020/12/15
 * @Version:1.0.0
 */
public class MyTask extends QuartzJobBean {
    
    

    @Autowired
    private TaskDefineMapper taskDefineMapper;

    /**
     * @Author LiDong
     * @Description //TODO 从jobExecutionContext 中拿出当前task的code
     * @Date 22:07 2020/12/22
     * @Param [jobExecutionContext]
     * @return void
     **/
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    
    
        //TODO 这里写定时任务的执行逻辑
        JobExecutionContextImpl jobDetail= ((JobExecutionContextImpl) jobExecutionContext);
        Trigger trigger = jobDetail.getTrigger();
        // 获得任务编码
        String taskcode = trigger.getJobKey().getName();
        dealJob(taskcode);
    }

    /**
     * @Author LiDong
     * @Description //TODO 执行url的具体逻辑
     * @Date 21:33 2020/12/23
     * @Param [taskcode]
     * @return void
     **/
    private void dealJob(String taskcode) {
    
    
        Map<String,String> map = new HashMap<>(1);
        map.put("taskcode",taskcode);
        TaskInfo taskDefine = taskDefineMapper.getTaskDefine(map);
        if(Objects.nonNull(taskDefine)) {
    
    
            String url = taskDefine.getUrl();
            String method = taskDefine.getMethod();
            String urlparams = taskDefine.getUrlparams();
            Map<String, String> param = JSONObject.parseObject(urlparams, Map.class);

            System.out.println("动态的定时任务执行时间:" + new Date().toLocaleString());
        }
    }
}

Controller、Service、ServiceImpl 部分代码

  • QuartzController
@RestController
@RequestMapping("/quartz")
public class QuartzController extends BaseController {
    
    

    /**
     * 注入任务调度
     **/
    @Resource
    private Scheduler scheduler;

    @Autowired
    private QuartzService quartzService;

    @RequestMapping("/createJob")
    public ResponseInfo createJob()  {
    
    
        return quartzService.createJob(scheduler,getParamMap(request),getCurrentUser());
    }

    @RequestMapping("/pauseJob")
    public ResponseInfo  pauseJob()  {
    
    
        return quartzService.pauseJob(scheduler,getParamMap(request),getCurrentUser());
    }@RestController
@RequestMapping("/quartz")
public class QuartzController extends BaseController {
    
    

    /**
     * 注入任务调度
     **/
    @Resource
    private Scheduler scheduler;

    @Autowired
    private QuartzService quartzService;

    @RequestMapping("/createJob")
    public ResponseInfo createJob()  {
    
    
        return quartzService.createJob(scheduler,getParamMap(request),getCurrentUser());
    }

    @RequestMapping("/pauseJob")
    public ResponseInfo  pauseJob()  {
    
    
        return quartzService.pauseJob(scheduler,getParamMap(request),getCurrentUser());
    }
    
}
  • QuartzServiceImpl
@Service
public class QuartzServiceImpl implements QuartzService {
    
    

    private static final Logger logger = LoggerFactory.getLogger(QuartzServiceImpl.class);

    @Autowired
    private TaskDefineMapper taskDefineMapper;

    /**
     * @Author LiDong
     * @Description //TODO 创建一个任务
     * @Date 20:45 2020/12/22
     * @Param [quartzBean, paramMap, currentUser]
     * @return com.li.core.config.globalexception.ResponseInfo
     **/
    @Override
    public ResponseInfo createJob(Scheduler scheduler, Map<String, String> paramMap, CurrentUser currentUser) {
    
    
        ResponseInfo result = new ResponseInfo();
        try {
    
    
            TaskInfo taskInfo=getTaskInfo(paramMap,currentUser,result);
            if(Objects.nonNull(taskInfo)){
    
    
                // 定义QuartzBean
                QuartzBean quartzBean = new QuartzBean();
                // 定义任务执行的类
                quartzBean.setJobClass("com.li.taskcenter.controller.quartz.MyTask");
                // 定义任务的名称唯一
                quartzBean.setJobName(taskInfo.getTaskcode());
                // corn表达式
                quartzBean.setCronExpression(taskInfo.getCorn());
                QuartzUtils.createScheduleJob(scheduler,quartzBean);
            }
            return result;
        } catch (Exception e) {
    
    
            logger.info(e.getMessage());
            return result;
        }
    }

    private TaskInfo getTaskInfo(Map<String, String> paramMap, CurrentUser currentUser, ResponseInfo result) {
    
    
        // 任务定义Id
        String taskdefineid = paramMap.getOrDefault("taskdefineid", null);
        if(StringUtils.isEmpty(taskdefineid)){
    
    
            result.setCode(500);
            result.setMessage("任务定义Id为空!");
        }
        Map<String,String> map = new HashMap<>(1);
        map.put("taskdefineid",taskdefineid);
        TaskInfo taskInfo =taskDefineMapper.getTaskDefine(map);
        return taskInfo;
    }

    /**
     * @Author LiDong
     * @Description // 停用一个任务
     * @Date 20:45 2021/1/5
     * @Param [scheduler, paramMap, currentUser]
     * @return com.li.core.config.globalexception.ResponseInfo
     **/
    @Override
    public ResponseInfo pauseJob(Scheduler scheduler, Map<String, String> paramMap, CurrentUser currentUser) {
    
    
        ResponseInfo responseInfo = new ResponseInfo();
        try {
    
    
            TaskInfo taskInfo=getTaskInfo(paramMap,currentUser,responseInfo);
            if(Objects.nonNull(taskInfo)){
    
    
                String taskcode = taskInfo.getTaskcode();
                QuartzUtils.pauseScheduleJob (scheduler,taskcode);
                responseInfo.setCode(200);
                responseInfo.setMessage(taskcode+"任务暂停成功!");
                return responseInfo;
            }
            responseInfo.setMessage("当前任务暂停失败!");
            return responseInfo;
        } catch (Exception e) {
    
    
            responseInfo.setCode(500);
            responseInfo.setMessage(e.getMessage());
            return responseInfo;
        }
    }
}

在这里插入图片描述
完整代码见地址https://gitee.com/Marlon_Brando/back/tree/master/OnlineShop

猜你喜欢

转载自blog.csdn.net/qq_37248504/article/details/112972378