SpringBoot集成Quartz发布、修改、暂停、删除定时任务

一、基本概念

Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节。

scheduler任务调度:

是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。

Job任务:

其实Job是接口,其中只有一个execute方法:

Trigger触发器

a)作用:它是来执行工作任务,在什么条件下触发,什么时间执行,多久执行一次。

b)四大类型:SimpleTrigger,CronTirgger,DateIntervalTrigger, 和 NthIncludedDayTrigger。

SimpleTrigger 一般用于实现每隔一定时间执行任务,以及重复多少次,如每 2 小时执行一次,重复执行 5 次。SimpleTrigger 内部实现机制是通过计算间隔时间来计算下次的执行时间,这就导致其不适合调度定时的任务。例如我们想每天的 1:00AM 执行任务,如果使用 SimpleTrigger 的话间隔时间就是一天。注意这里就会有一个问题,即当有 misfired 的任务并且恢复执行时,该执行时间是随机的(取决于何时执行 misfired 的任务,例如某天的 3:00PM)。这会导致之后每天的执行时间都会变成 3:00PM,而不是我们原来期望的 1:00AM。

CronTirgger 类似于 LINUX 上的任务调度命令 crontab,即利用一个包含 7 个字段的表达式来表示时间调度方式。例如,"0 15 10 * * ? *" 表示每天的 10:15AM 执行任务。对于涉及到星期和月份的调度,CronTirgger 是最适合的,甚至某些情况下是唯一选择。例如,"0 10 14 ? 3 WED" 表示三月份的每个星期三的下午 14:10PM 执行任务。读者可以在具体用到该 trigger 时再详细了解每个字段的含义

整合案例

项目结构


1562652375822.png

listener的作用

每次项目重启后,让原来处于运行中的任务,继续运行

QuartzManager的作用

对定时任务执行具体操作的工具类

依赖

 
            org.quartz-scheduler
            quartz
            2.3.1
        
        
            org.quartz-scheduler
            quartz-jobs
            2.3.1
        

application.yml配置

#quartz
spring:
  quartz:
    scheduler-name: my-project-scheduler
    job-store-type: jdbc
    auto-startup: false
    wait-for-jobs-to-complete-on-shutdown: false
    overwrite-existing-jobs: false
    jdbc:
      #druid的wall filter会影响这里的自动建表
      initialize-schema: always
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO
            wrapJobExecutionInUserTransaction: false
            rmi:
              export: false
              proxy: false
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            useProperties: true
            tablePrefix: QRTZ_
            misfireThreshold: 60000

quartzManager工具类

package com.nanc.modules.schedule.util;
import com.nanc.modules.schedule.entity.ScheduleJobInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class QuartzManager {
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    public static final String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME";
    public static final String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME";

    /**
     * 添加任务,使用任务组名(不存在就用默认的),触发器名,触发器组名
     * 并启动
     * @param info
     */
    public void addJob(ScheduleJobInfo info) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();

            JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (null != jobDetail) {
                log.info("{}, {} 定时任务已经存在", info.getJobName(), info.getGroupName());
                return;
            }

            JobDataMap map = new JobDataMap();
            map.put("taskData", "测试的存放的数据");

            // JobDetail 是具体Job实例
            jobDetail = JobBuilder.newJob((Class) Class.forName(info.getClassName()))
                    .withIdentity(info.getJobName(),info.getGroupName())
                    .usingJobData(map)
                    .build();

            // 基于表达式构建触发器
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(info.getCron());
            // CronTrigger表达式触发器 继承于Trigger
            // TriggerBuilder 用于构建触发器实例
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(info.getJobName()+"_trigger", TRIGGER_GROUP_NAME)
                    .withSchedule(cronScheduleBuilder).build();

            scheduler.scheduleJob(jobDetail, cronTrigger);

            //启动
            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (SchedulerException | ClassNotFoundException e) {
            log.error("添加任务失败",e);
        }
    }

    /**
     * 暂停任务
     * @param info
     */
    public void pauseJob(ScheduleJobInfo info) {
        try{
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
            scheduler.pauseJob(jobKey);
            log.info("=========================pause job: {} success========================", info.getJobName());
        }catch (Exception e){
            log.error("",e);
        }
    }

    /**
     * 恢复任务
     * @param info
     */
    public void resumeJob(ScheduleJobInfo info) {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
            scheduler.resumeJob(jobKey);
            log.info("=========================resume job: {} success========================", info.getJobName());
        }catch (Exception e){
            log.error("",e);
        }
    }

    /**
     * 删除任务,在业务逻辑中需要更新库表的信息
     * @param info
     * @return
     */
    public boolean removeJob(ScheduleJobInfo info) {
        boolean result = true;
        try {

            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jobKey = JobKey.jobKey(info.getJobName(), info.getGroupName());
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (null != jobDetail) {
                result =  scheduler.deleteJob(jobKey);
            }
            log.info("=========================remove job: {} {}========================", info.getJobName(), result);
        }catch (Exception e){
            log.error("",e);
            result=false;
        }
        return result;
    }

    /**
     * 修改定时任务的时间
     * @param info
     * @return
     */
    public boolean modifyJobTime(ScheduleJobInfo info){
        boolean result = true;
        try{
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(info.getJobName() + "_trigger", TRIGGER_GROUP_NAME);
            CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);

            String oldTime = trigger.getCronExpression();
            if (!StringUtils.equalsIgnoreCase(oldTime, info.getCron())) {
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(info.getCron());
                CronTrigger ct = TriggerBuilder.newTrigger().withIdentity(info.getJobName()+ RandomStringUtils.randomAlphabetic(6) + "_trigger", TRIGGER_GROUP_NAME)
                        .withSchedule(cronScheduleBuilder)
                        .build();

                scheduler.rescheduleJob(triggerKey, ct);
                scheduler.resumeTrigger(triggerKey);
            }

        }catch (Exception e){
            log.error("", e);
            result=false;
        }
        return result;
    }

    /**
     * 启动所有定时任务
     */
    public void startJobs() {
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            scheduler.start();
        } catch (SchedulerException e) {
            log.error("",e);
        }
    }
}

定义的实体类

package com.nanc.modules.schedule.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("schedule_job_info")
@ApiModel(description = "定时任务实体类")
public class ScheduleJobInfo implements Serializable {
    private static final long serialVersionUID = -1465015133146616824L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 任务group的名称
     */
    @NotEmpty(message = "任务group的名称不能为空")
    @ApiModelProperty(value = "任务group的名称", example = "test-group")
    private String groupName;

    /**
     * 任务job的名称
     */
    @NotEmpty(message = "任务job的名称不能为空")
    @ApiModelProperty(value = "任务job的名称", required = true, example = "test-job")
    private String jobName;

    /**
     * 定时任务标识
     */
    @NotEmpty(message = "定时任务标识不能为空")
    @ApiModelProperty(value = "任务的标识", required = true, example = "schedule-job-code")
    private String code;

    /**
     * cron 表达式
     */
    @NotEmpty(message = "定时任务标识不能为空")
    @ApiModelProperty(value = "cron表达式", required = true, example = "*/5 * * * * ?")
    private String cron;

    /**
     * 定时任务执行类
     */
    @NotEmpty(message = "定时任务标识不能为空")
    @ApiModelProperty(value = "定时任务执行类", required = true, example = "com.nanc.modules.schedule.job.CronTestJob")
    private String className;

    /**
     * 成功执行次数
     */
    @ApiModelProperty(hidden = true)
    private Integer succeed;

    /**
     * 失败执行次数
     */
    @ApiModelProperty(hidden = true)
    private Integer fail;

    /**
     * 任务的状态
     *      0 - 代表正在执行
     *      1 - 已删除
     *      2 - 暂停
     */
    @ApiModelProperty(hidden = true)
    private Integer status;


    /**
     * 任务创建的时间
     */
    @ApiModelProperty(hidden = true)
    private Date createTime;


    /**
     * 任务修改的时间
     */
    @ApiModelProperty(hidden = true)
    private Date updateTime;
}

控制层代码

package com.nanc.modules.schedule.controller;
import com.nanc.common.utils.R;
import com.nanc.modules.schedule.entity.ScheduleJobInfo;
import com.nanc.modules.schedule.service.ScheduleJobService;
import com.nanc.modules.schedule.util.QuartzManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
@Slf4j
@RestController
@RequestMapping("/quartz")
@Api(value = "定时任务", tags = "定时任务", position = 5)
public class ScheduleJobController {
    @Autowired
    private QuartzManager quartzManager;
    @Autowired
    private ScheduleJobService scheduleJobService;

    @ApiOperation(value = "添加定时任务并启动", notes = "添加定时任务并启动")
    @PostMapping("/add")
    public R addAndStartJob(@Valid @RequestBody ScheduleJobInfo info){
        boolean b = scheduleJobService.addAndStartJob(info);
        return R.ok(b);
    }

    /**
     * 暂停任务的运行
     * @param id
     * @return
     */
    @ApiOperation(value = "暂停任务", notes = "暂停任务")
    @PostMapping("/pause/{id}")
    public R pauseJob(@PathVariable(value = "id") Integer id) {
        boolean b = scheduleJobService.pauseJob(id);
        return R.ok(b);
    }

    /**
     * 暂停任务的运行
     * @param id
     * @return
     */
    @ApiOperation(value = "恢复任务", notes = "恢复任务")
    @PostMapping("/resume/{id}")
    public R resumeJob(@PathVariable(value = "id") Integer id) {
        boolean b = scheduleJobService.resumeJob(id);
        return R.ok(b);
    }

    /**
     * 删除任务
     * @param id
     * @return
     */
    @ApiOperation(value = "删除任务", notes = "删除任务")
    @PostMapping("/remove/{id}")
    public R removeJob(@PathVariable(value = "id") Integer id) {
        boolean b = scheduleJobService.removeJob(id);
        return R.ok(b);
    }

    /**
     * 修改定时任务的时间
     * @param id
     * @param cron
     * @return
     */
    @ApiOperation(value = "修改任务时间", notes = "修改任务时间")
    @PostMapping("/modify")
    public R modifyJobTime(@RequestParam(value = "id")Integer id,
                                    @RequestParam(value = "cron")String cron){

        boolean b = scheduleJobService.modifyJobTime(id, cron);
        return R.ok(b);
    }
}

关于swagger的整合,可以看另一篇文章

目前只是简单的实现了部分常用的方法

需要源代码的私信,我会从项目中把这模块提取出来,单独打包发送

你可能感兴趣的:(SpringBoot集成Quartz发布、修改、暂停、删除定时任务)