Quartz定时任务管理(动态添加、停止、恢复、删除定时任务)

Quartz定时任务框架经常用于系统后台业务异步处理。平常我们使用时,主要是通过手工编写配置代码文件方式添加修改定时任务,然后重启系统。有时候我们需要根据业务运营需要,动态添加修改定时任务,比如添加新的定时任务、修改任务执行时间、暂停定时任务、删除定时任务等,并且监控定时任务状态,而又不想重启系统,这时就需要系统具备动态管理定时任务的功能。
Quartz提供了一系列组件,支持动态管理定时任务的功能。
Quartz定时任务主要由Scheduler、JobDetail、CronTrigger、Cron组成,实现动态管理定时任务,主要就是通过管理上述对象来实现的。
1、数据库设计
主要将我们平时配置的任务计划放入数据库中保存。在启动任务是,从数据库中查找任务计划信息,并动态配置进去即可。

DROP TABLE IF EXISTS `cc_task_info`;
CREATE TABLE `cc_task_info` (
  `TID` int(11) NOT NULL AUTO_INCREMENT,
  `TASK_ANME` varchar(50) NOT NULL,
  `TASK_CODE` varchar(50) NOT NULL,
  `JOB_CLASS` varchar(200) NOT NULL,
  `JOB_GROUP` varchar(50) NOT NULL,
  `CRON` varchar(50) NOT NULL,
  `DEL_STATUS` varchar(2) DEFAULT '1' NULL,
  `CRT_TIME` datetime DEFAULT NULL,
  PRIMARY KEY (`TID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务管理表';

DROP TABLE IF EXISTS `cc_task_record`;
CREATE TABLE `cc_task_record` (
  `RID` int(11) NOT NULL AUTO_INCREMENT,
  `TASK_CODE` varchar(50) NOT NULL,
  `RUN_TIME` datetime NOT NULL,
  `RUN_CODE` char(1) NOT NULL,
  `RUN_MSG` varchar(100) NULL,
  PRIMARY KEY (`RID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务运行记录表';

DROP TABLE IF EXISTS `cc_task_status`;
CREATE TABLE `cc_task_status` (
  `TASK_CODE` varchar(50) NOT NULL,
  `TASK_STATUS` varchar(10) NOT NULL,
  `LST_SUCC_TIME` datetime NOT NULL,
  `LST_TIME` datetime NOT NULL,
  PRIMARY KEY (`TASK_CODE`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务运行状态表';

2、定时任务管理
定时任务管理主要是通过Scheduler的方法来实现。Scheduler提供了一系列方法来管理定时任务的执行状态。主要包括:
scheduleJob():添加定时任务
rescheduleJob():修改定时任务
pauseJob():暂停定时任务执行
resumeJob():恢复定时任务执行
deleteJob():删除定时任务执行
针对上述方法,我们只需要传入对应参数即可。
这里我建了一个QuartzService来管理定时任务,供业务层调用。
详细代码如下:

/**
 * 定时任务管理服务
 */
@Service
public class QuartzService {

    public static String SCHEDULER_OPR_START = "start";
    public static String SCHEDULER_OPR_PAUSE = "pause";
    public static String SCHEDULER_OPR_RESUME = "resume";
    public static String SCHEDULER_OPR_REMOVE = "remove";

    @Autowired
    private Scheduler scheduler;

    /**
     * 启动任务
     */
    public void startJob(String taskCode, String taskAnme, String cron, String jobGroup,
                         String className) throws Exception{
        Class<Job> jobClass = null;
        try {
            jobClass = (Class<Job>) Class.forName(className);//获取任务执行类
        } catch (ClassNotFoundException e) {
            throw new Exception("任务类不存在");
        }
        //创建job,指定job名称和分组
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(taskCode, jobGroup).build();
        //创建表达式工作计划
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        //创建触发器
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup)
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 修改定时任务执行时间
     * @param taskCode
     * @param jobGroup
     * @param cron 新的时间
     * @throws Exception
     */
    public void modifyJob(String taskCode, String jobGroup, String cron) throws Exception{
        TriggerKey triggerKey = new TriggerKey(taskCode, jobGroup);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldCron = trigger.getCronExpression();
        if(!oldCron.equals(cron)){
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup)
                    .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
            Date date = scheduler.rescheduleJob(triggerKey, cronTrigger);
            if(date == null){
                throw new Exception("修改定时任务执行时间报错");
            }
        }
    }

    /**
     * 暂停某个定时任务(任务恢复后,暂停时间段内未执行的任务会继续执行,如暂停时间段内有2次,则会执行2次)
     * @param taskCode
     * @param jobGroup
     * @throws Exception
     */
    public void pauseJob(String taskCode, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(taskCode, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if(jobDetail == null){
            return;
        }
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复某个定时任务
     * @param taskCode
     * @param jobGroup
     * @throws Exception
     */
    public void resumeJob(String taskCode, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(taskCode, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if(jobDetail == null){
            return;
        }
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除某个定时任务
     * @param taskCode
     * @param jobGroup
     * @throws Exception
     */
    public void deleteJob(String taskCode, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(taskCode, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if(jobDetail == null){
            return;
        }
        scheduler.deleteJob(jobKey);
    }
}

3、编写任务类JOB
任务类JOB就是定时任务具体要处理的系统业务逻辑,需要实现Job接口。在任务启动时,通过jobClass传入JobDetail。

public class DemoJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String taskCode = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.println("执行定时任务:" + taskCode);
    }
}

4、配置Scheduler
在Configuration中配置Scheduler实例,并启动。

@Configuration
public class QuartzConfig {

    @Bean
    public Scheduler scheduler(){
        Scheduler scheduler = null;
        SchedulerFactory factory = new StdSchedulerFactory();
        try {
            scheduler = factory.getScheduler();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        if(scheduler != null){
            try {
                //启动定时任务
                scheduler.start();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }
        return scheduler;
    }
}

5、编写API接口
通过Controller提供API接口,这里我的TaskService调用了QartzService的对应接口,并做了一个写数据库读写操作,主要记录定时任务状态、执行记录信息的等。

@RestController
@RequestMapping("/api/task")
public class TaskController {

    @Autowired
    private TaskService service;

    @RequestMapping("/start")
    public Object start(int id){
        try {
            service.startJob(id);
            return RtnData.ok();
        } catch (Exception e) {
            return RtnData.fail(e.getMessage());
        }
    }

    @RequestMapping("/pause")
    public Object pause(int id){
        try {
            service.pauseJob(id);
            return RtnData.ok();
        } catch (Exception e) {
            return RtnData.fail(e.getMessage());
        }
    }

    @RequestMapping("/resume")
    public Object resume(int id){
        try {
            service.resumeJob(id);
            return RtnData.ok();
        } catch (Exception e) {
            return RtnData.fail(e.getMessage());
        }
    }

    @RequestMapping("/remove")
    public Object remove(int id){
        try {
            service.deleteJob(id);
            return RtnData.ok();
        } catch (Exception e) {
            return RtnData.fail(e.getMessage());
        }
    }
}

6、接口测试
先在数据库中将DemoJob添加到任务管理表中,然后使用postman调用api进行任务启动、修改、暂停、恢复、删除等操作,观察系统后台日志打印情况查看效果。
数据库初始化如下:
《Quartz定时任务管理(动态添加、停止、恢复、删除定时任务)》

    原文作者:古甲哈醒
    原文地址: https://blog.csdn.net/cjbfzxz/article/details/114116440
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞