# spring boot 配置 quartz

# 导入依赖

Quartz依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
    <version>2.6.7</version>
</dependency>

# 配置文件

application.yml
server:
  port: 8090
spring:
  datasource:
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password: root
      url: jdbc:mysql://127.0.0.1:3306/quartz?characterEncoding=utf-8&useSSL=false
  quartz:
    jdbc:
      initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。always: 总是,never: 不需要
    job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
    properties:
      org:
        quartz:
          scheduler:
            instanceName: QuartzScheduler # 调度标识名 集群中每一个实例都必须使用相同的名称
            instanceId: AUTO # 定时任务的实例编号,如果手动指定需要保证每个节点的唯一性
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 100  # 线程池大小。默认为 10 。
            threadPriority: 5 # 线程优先级
          jobStore:
            misfireThreshold: 120000

# 代码实现

# 编写工具类 QuartzCronDateUtils 日期转换 cron 表达式

public class QuartzCronDateUtils {
	/*** 
     *  功能描述:日期转换 cron 表达式时间格式
     * @param date 
     * @param dateFormat : e.g:yyyy-MM-dd HH:mm:ss 
     * @return 
     */  
    public static String formatDateByPattern(Date date,String dateFormat){  
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);  
        String formatTimeStr = null;  
        if (date != null) {  
            formatTimeStr = sdf.format(date);  
        }  
        return formatTimeStr;  
    }  
    /*** 
     * convert Date to cron ,eg.  "14 01 17 22 07 ? 2017" 
     * @param date: 时间点 
     * @return 
     */  
    public static String getCron(java.util.Date  date){  
        String dateFormat="ss mm HH dd MM ? yyyy";  
        return formatDateByPattern(date,dateFormat);  
    }  
}

# 编写动态任务调度管理器工具类,实现 CRUD 管理定时器

public class QuartzManager {
    private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    /**
     * 功能: 添加一个定时任务
     * @param jobName 任务名
     * @param jobGroupName  任务组名
     * @param triggerName 触发器名
     * @param triggerGroupName 触发器组名
     * @param jobClass  任务的类类型  eg:TimedMassJob.class
     * @param cron   时间设置 表达式,参考 quartz 说明文档
     * @param objects  可变参数需要进行传参的值
     */
    public static void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron,Object...objects) {
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
            // 触发器
            if(objects!=null){
                for (int i = 0; i < objects.length; i++) {
                    // 该数据可以通过 Job 中的 JobDataMap dataMap = context.getJobDetail ().getJobDataMap (); 来进行参数传递值
                    jobDetail.getJobDataMap().put("data"+(i+1), objects[i]);
                }
            }
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            // 触发器名,触发器组
            triggerBuilder.withIdentity(triggerName,triggerGroupName);
            triggerBuilder.startNow();
            // 触发器时间设定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
            // 创建 Trigger 对象
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();
            // 调度容器设置 JobDetail 和 Trigger
            scheduler.scheduleJob(jobDetail, trigger);
            // 启动
            if (!scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 功能:修改一个任务的触发时间
     * @param jobName
     * @param jobGroupName
     * @param triggerName 触发器名
     * @param triggerGroupName 触发器组名
     * @param cron   时间设置,参考 quartz 说明文档
     */
    public static void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                // 触发器
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();
                // 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                // 创建 Trigger 对象
                trigger = (CronTrigger) triggerBuilder.build();
                // 方式一 :修改一个任务的触发时间
                scheduler.rescheduleJob(triggerKey, trigger);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 功能:移除一个任务
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     */
    public static void removeJob(String jobName, String jobGroupName,String triggerName, String triggerGroupName) {
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName,triggerGroupName);
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(JobKey.jobKey(jobName,jobGroupName));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     *
     * 功能:启动所有定时任务
     */
    public static void startJobs() {
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 功能:关闭所有定时任务
     */
    public static void shutdownJobs() {
        try {
            Scheduler scheduler = schedulerFactory.getScheduler();
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static List getTotal() throws SchedulerException {
        Scheduler scheduler = schedulerFactory.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = null;
        List jobList = new ArrayList();
        try {
            jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    HashMap job = new HashMap();
                    job.put("jobDetailName",jobKey.getName());
                    job.put("groupName",jobKey.getGroup());
                    job.put("jobCronExpression","触发器:" + trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.put("status",triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.put("jobCronExpression",cronExpression);
                    }
                    jobList.add(job);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }
}

# 自定义 Job,实现 org.quartz.Job 接口

data : 是我们需要处理的数据

在执行后取消定时任务

@Slf4j
public class TimedMassJob implements org.quartz.Job {
    @SneakyThrows
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String job=dataMap.getString("data1");
        String jobName =job+"Job";
        String jobGroupName =job+"JobGroup";
        String triggerName =job+"Trigger";
        String triggerGroupName =job+"TriggerGroup";
        String chapterName =dataMap.getString("data2");
        log.info("所有的定时任务:{}",QuartzManager.getTotal().toString());
        log.info("执行定时任务时间{},接收到到的数据{}",new Date(),chapterName);
        log.info("开始销毁:JobName={},JobGroupName={},triggerName={},triggerGroupName={}",jobName,jobGroupName,triggerName,triggerGroupName);
        QuartzManager.removeJob(jobName,jobGroupName,triggerName,triggerGroupName);
        log.info("所有的定时任务:{}",QuartzManager.getTotal().toString());
    }
}

# Controller 层实现业务接口

addJob() 最后有可变长参数来传递参数

创建Job
@Slf4j
@RestController
@RequestMapping("/test")
public class ScheduleController {
    
    /**
     * 更新 cron
     * http://127.0.0.1:8090/test/updateCron
     */
    @GetMapping("/updateCron")
    public void updateCron() {
        Date date =new Date();
        Date afterDate = new Date(date .getTime() + 60000);
        QuartzManager.addJob("testJob", "testJobGroup", "testTrigger", "testTriggerGroup", TimedMassJob.class, QuartzCronDateUtils.getCron(afterDate),"test","asdasd");
    }
    public static void main(String[] args) {
        QuartzManager.removeJob("testJob", "job", "testTrigger", "testTriggerGroup");
    }
}

# 相关参数说明

# 定时任务(Job)

定时任务是指需要按照一定的时间规则来执行的任务,比如每天早上 6 点执行一次数据备份操作。在 Quartz 中,一个定时任务由以下几个部分组成:

  • Job:定义任务执行的逻辑代码,需要实现 Job 接口并重写 execute 方法。
  • JobDetail:封装 Job 对象和任务相关的元数据,包括任务名称、任务组、Job 实现类等信息。
  • JobDataMap:存储任务执行所需的参数,可以在 JobDetail 中设置,也可以在触发器中设置。

# 触发器 (Trigger)

触发器是用来触发定时任务执行的,它定义了任务应该在何时执行,如何执行等信息。在 Quartz 中,一个触发器由以下几个部分组成:

  • Trigger:定义任务触发的时间规则,如何触发任务等信息。
  • TriggerBuilder:用于构建 Trigger 对象,可以设置触发器的名称、触发器组、触发时间、触发规则等信息。
  • Cron 表达式:定义触发器的触发时间规则,可以设置年、月、日、周、时、分、秒等。

# 持久化到数据库

配置 quartz.properties

quartz的配置文件
# ThreadPool 中的简单线程池 SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程池中线程数量
org.quartz.threadPool.threadCount = 5
#线程的优先级 10 最高
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 5000
#存储方式使用 JobStoreTX,也就是数据库
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库中 quartz 表的表名前缀
org.quartz.jobStore.tablePrefix = QRTZ_
#配置数据源
org.quartz.jobStore.dataSource = qzDS
org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = root
org.quartz.dataSource.qzDS.maxConnections = 10

超过八小时会出现当使用 Quartz 调度器时,如果遇到连接超时和 wait_timeout 问题.

解决:Quartz 调度器出现链接超时和 wait_timeout 问题