Quartz 调度框架介绍

Quartz 介绍

Quartz 是一个开源的作业调度框架,可以让计划的程序任务一个预定义的日期和时间运行。

Quartz 可以用来创建简单或复杂的日程安排执行几十,几百,甚至是十万的作业数。

Quartz 官网:http://www.quartz-scheduler.org/

GitHub:Quartz Job Scheduler · GitHub

Quartz 功能

如果应用程序需要在给定时间执行任务,或者如果系统有连续维护作业,那么 Quartz 是理想的解决方案。

使用 Quartz 作业调度应用的示例:

  • 驱动处理工作流程:作为一个新的订单被初始化放置,调度作业到在正好两个小时内,它将检查订单的状态,如果订单确认消息尚未收到命令触发警告通知,以及改变订单的状态为 “等待的干预”。
  • 系统维护:调度工作给数据库的内容,每个工作日(节假日除外平日)在 11:30 PM 转储到一个 XML 文件中。
  • 在应用程序内提供提醒服务

运行环境

  • Quartz 可以运行嵌入在另一个独立式应用程序
  • Quartz 可以在应用程序服务器 (或 servlet 容器) 内被实例化,并且参与 XA 事务
  • Quartz 可以作为一个独立的程序运行 (其自己的 Java 虚拟机内),可以通过 RMI 使用
  • Quartz 可以被实例化,作为独立的项目集群 (负载平衡和故障转移功能),用于作业的执行

作业调度

作业被安排在一个给定的触发时运行。触发器可以使用以下指令的接近任何组合来创建:

  • 在一天中的某个时间(到毫秒)
  • 在一周的某几天
  • 在每月的某一天
  • 在一年中的某些日期
  • 不在注册的日历中列出的特定日期(如商业节假日除外)
  • 重复特定次数
  • 重复进行,直到一个特定的时间 / 日期
  • 无限重复
  • 重复的延迟时间间隔

作业是由其创建者赋予的名字,也可以组织成命名组。触发器也可以给予名称和放置在组中,以方便地将它们调度内组织。

作业可以被添加到所述调度器一次,而是具有多个触发器注册。

在企业 Java 环境中,作业可以执行自己的工作作为分布式(XA)事务的一部分。

作业执行

  • 作业可以实现简单的作业接口,为作业执行工作的任何 Java 类。
  • Job 类的实例可以通过 Quartz 被实例化,或者通过应用程序框架。
  • 当触发时,调度通知实现 JobListener 和 TriggerListener 接口零个或多个 Java 对象(监听器可以是简单的 Java 对象,或 EJB,JMS 或发布者等)。这些监听器在作业已经执行之后通知。
  • 由于作业完成后返回 JobCompletionCode,它通知的成功或失败的调度。JobCompletionCode 还可以指示的基础上,成功的话就采取行动调度 / 失败的代码 - 如立即重新执行作业。

作业持久性

  • Quartz 的设计包括可被实现以提供的作业存储各种机制一个作业存储接口
  • 通过使用包含的 JDBCJobStore,所有的作业和触发器配置为 “非挥发性” 都存储在通过 JDBC 关系数据库。
  • 通过使用包含的 RAMJobStore,所有的作业和触发器存储在 RAM,因此不计划执行仍然存在 - 但这是无需使用外部数据库的优势。

事务

  • 可以参与 JTA 事务,通过使用 JobStoreCMT(JDBCJobStore 的子类)。
  • Quartz 可以管理 JTA 事务(开始并提交它们)周围作业的执行,从而使作业执行的工作自动将 JTA 事务中发生。

集群

  • 故障切换
  • 负载均衡
  • Quartz 的内置的群集功能,通过 JDBCJobStore(如上所述)依靠数据库持久
  • Terracotta 扩展 Quartz 提供集群功能,而不需要一个支持数据库

监听器和插件

  • 应用程序可以捕捉事件的调度监控或通过实现一个或多个监听器接口控制工作 / 触发行为。
  • 插件机制,可以用来添加功能,Quartz 让作业执行过程中或工作负载和触发定义的历史不受限在一个文件中。
  • 附带了一些 “工厂建有” 插件和监听器。

特点

  1. 支持分布式高可用,我们需要某个定时任务在多个节点中只有某个节点可以执行时,就需要Quartz来实现,否则使用@Scheduled等方式会造成所有节点都执行一遍。
  2. 支持持久化,Quartz有专门的数据表来实现定时任务的持久化。
  3. 支持多任务调度和管理,Quartz可以在数据库中存储多个定时任务进行作业调度,可以实现定时任务的增删改查等管理。

组成

  Quartz由三部分组成:

  1. 任务:JobDetail
  2. 触发器:Trigger(分为SimpleTrigger和CronTrigger)
  3. 调度器:Scheduler

JobDetail

  JobDetail主要由JobKey(job的名字name和分组group)、JobClass、JobDataMap(任务相关的数据)、JobBuilder组成。常用的是前几个。

JobDetail源码

package org.quartz;

import java.io.Serializable;

public interface JobDetail extends Serializable, Cloneable {
    JobKey getKey();

    String getDescription();

    Class<? extends Job> getJobClass();

    JobDataMap getJobDataMap();

    boolean isDurable();

    boolean isPersistJobDataAfterExecution();

    boolean isConcurrentExectionDisallowed();

    boolean requestsRecovery();

    Object clone();

    JobBuilder getJobBuilder();
}


JobDetail示例

		Map<String,String> jobData = new HashMap<>();
		String jobName = "schedulerJob";
		String jobGroup = "schedulerGroup";
		jobData.put("key00", "value00");
        JobDetail jobDetail = JobBuilder.newJob(SchedulerJob.class)
                .withIdentity(jobName, jobGroup)
                .usingJobData("key01", "value01")
                .usingJobData(jobData)
                .storeDurably()
                .build();

Trigger

  Trigger规定触发执行Job实现类,主要有SimpleTrigger和CronTrigger两个实现类。Trigger由以下部分组成:

  1. TriggerKey(job的名字name和分组group)
  2. JobDataMap(Trigger相关的数据,同JobDetail中JobDataMap,存相同key,若value不同,会覆盖前者。)
  3. ScheduleBuilder(有CronScheduleBuilder、SimpleScheduleBuilder、CalendarIntervalScheduleBuilder、DailyTimeIntervalScheduleBuilder常用前2种。)

    Trigger示例
	//SimpleScheduleBuilder
	String triggerName = "schedulerJob";
	String triggerGroup = "schedulerGroup";
	Trigger trigger = TriggerBuilder
	    .newTrigger()
	    .withIdentity(triggerName, triggerGroup)
	    .withSchedule(SimpleScheduleBuilder)
	    .repeatSecondlyForever(1)
	    .withIntervalInSeconds(0)
	    .withRepeatCount(0))
	    .startNow()
	    .build();
	
	//CronScheduleBuilder
	String triggerName2 = "schedulerJob2";
	String triggerGroup2 = "schedulerGroup2";
	String jobTime = "0 0 * * * ?";
	Trigger trigger2 = TriggerBuilder
		.newTrigger()
		.withIdentity(triggerName2, triggerGroup2)
		.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
		.withSchedule(CronScheduleBuilder.cronSchedule(jobTime))
		.startNow()
		.build();

Scheduler

  调度器就是为了读取触发器Trigger从而触发定时任务JobDetail。可以通过SchedulerFactory进行创建调度器,分为StdSchedulerFactory(常用)和DirectSchedulerFactory两种。

  1. StdSchedulerFactory使用一组属性(放在配置文件中)创建和初始化调度器,然后通过getScheduler()方法生成调度程序。
  2. DirectSchedulerFactory不常用,容易硬编码。

    Scheduler示例
	//建好jobDetail,trigger
	... ...
	//StdSchedulerFactory方式,用的多
	SchedulerFactory schedulerFactory = new StdSchedulerFactory();
	Scheduler schedulerStd = schedulerFactory.getScheduler();
	
	//DirectSchedulerFactory方式
	DirectSchedulerFactory directSchedulerFactory = DirectSchedulerFactory.getInstance();
	Scheduler schedulerDir=directSchedulerFactory.getScheduler();
	
	//执行调度
	schedulerStd.scheduleJob(jobDetail, trigger);
	schedulerStd.start();

Cron表达式

  定时任务离不开Cron表达式设置具体执行时间或执行周期,Cron表达式是一个字符串,一般有两种表达:

  1. 秒 分 小时 日 月 星期 年
  2. 秒 分 小时 日 月 星期

其中,年份即为可选的,所以一般表达式为6-7个域,每个域以空格分开。其中的星期除了可以使用英文缩写,也可以用数字1-7数字来表示,注意1表示的是星期日7表示的星期六
各个域含义

  • *:星号,表示每个字段对应的时间域的每一个,如在日中,就是表示每天。
  • ?:问号,只能在日期和星期字段中使用,表示无意义的值,等价于点位符。
  • -:减号,表示一个范围,如在分钟中使用5-8,则表示5-8分钟,即5、6、7、8分钟。
  • ,:逗号,表示一个列表值,如在星期中星期一和星期三使用MON,WED,也可以使用数字来表示:1,3
  • /:斜杠,使用x/y来表示一个等步长序列,x表示起始值,y表示步长值。如在秒字段中使用0/15,表示从0秒开始,每15秒增量,即0秒,15秒,30秒,45秒,这种就可以理解为每15秒执行任务。
  • L:只能在日期和星期字段中使用,表示Last。在日期中,L表示月份的最后一天,如1月中的31日;在星期中,L表示星期六(或数字7)。
  • W:只能在日期字段中使用,表示离该日期最近的工作期,不可以跨月。如10W,表示离该月10号最近的工作日,若10号为星期六,则匹配9号星期五;若10号为星期日,则匹配11号星期一;若10号为星期一,则匹配10号星期一。LW组合表示该月的最后一个工作日。
  • C:只能在日期和星期字段中使用,表示Calendar,即计划所关联的日期,若日期未被关联,则等价于关联所有日期。如日期中使用4C,表示日期4号以后的第一天;星期中使用1C,表示星期日后的第一天。
  • #:井号只能在星期字段中使用,表示当月某个工作日。如6#2表示当月的第二个星期五(其中,6表示星期五,#3表示当月的第二个).

    Cron示例

|Cron表达式|说明|
|--|--|--|
|0 0 * * * ?|每小时0分0秒运行|
|0 0 1 * * ?|每天01:00:00运行运行|
|0 0 1 * * ? *|每天01:00:00运行运行,同上|
|0 0 1 * * ? 2021|2021年每天01:00:00运行|
|0 * 10 * * ?|每天10点-11点之间每分钟运行一次,开始于10:00:00,结束于10:59:00|
|0 0/5 10 * * ?|每天10点-11点之间每5分钟运行一次,开始于10:00:00,结束于10:59:00|
|0 0/5 10,15 * * ?|每天10点-11点之间每5分钟运行一次,每天15点-16点之间每5分钟运行一次|
|0 0-10 10 * * ?|每天10:00-10:10之间每分钟运行|
|0 10 1 ? * MON-FRI|每周一,二,三,四,五的1:10分运行|
|0 10 1 1 * ?|每月1日的1:10分运行|
|0 10 1 L * ?|每月最后一天1:10分运行|
|0 10 1 ? * 6L|每月最后一个星期五1:10分运行|
|0 10 1 ? * 6#3|每月第3个星期五1:10分运行|

Quartz增删改查模板

QuartzService接口类

package com.andya.selfcode.quartzservice;

import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.List;
import java.util.Map;

/**
 * @author Andya
 * @date 2021/4/01
 */
public interface QuartzService {

    /**
     * 增加一个任务job
     * @param jobClass  任务job实现类
     * @param jobName   任务job名称(保证唯一性)
     * @param jobGroupName  任务job组名
     * @param jobTime   任务时间间隔(秒)
     * @param jobTimes  任务运行次数(若<0,则不限次数)
     * @param jobData   任务参数
     */
    void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
                int jobTimes, Map jobData);

    /**
     * 增加一个任务job
     * @param jobClass  任务job实现类
     * @param jobName   任务job名称(保证唯一性)
     * @param jobGroupName  任务job组名
     * @param jobTime   任务时间表达式
     * @param jobData   任务参数
     */
    void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData);

    /**
     * 修改一个任务job
     * @param jobName 任务名称
     * @param jobGroupName  任务组名
     * @param jobTime   cron时间表达式
     */
    void updateJob(String jobName, String jobGroupName, String jobTime);

    /**
     * 删除一个任务job
     * @param jobName
     * @param jobGroupName
     */
    void deleteJob(String jobName, String jobGroupName);

    /**
     * 暂停一个任务job
     * @param jobName
     * @param jobGroupName
     */
    void pauseJob(String jobName, String jobGroupName);

    /**
     * 恢复一个任务job
     * @param jobName
     * @param jobGroupName
     */
    void resumeJob(String jobName, String jobGroupName);

    /**
     * 立即执行一个任务job
     * @param jobName
     * @param jobGroupName
     */
    void runAJobNow(String jobName, String jobGroupName);

    /**
     * 获取所有任务job
     * @return
     */
    List<Map<String, Object>> queryAllJob();

    /**
     * 获取正在运行的任务job
     * @return
     */
    List<Map<String, Object>> queryRunJob();


}

QuartzServiceImpl实现类

package com.andya.selfcode.quartz.service;

import com.andya.selfcode.quartz.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * @author Andya
 * @date 2021/4/01
 */
@Slf4j
@Service
public class QuartzServiceImpl implements QuartzService {

    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass
     *            任务实现类
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     * @param jobTime
     *            时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes
     *            运行的次数 (<0:表示不限次数)
     * @param jobData
     *            参数
     */
    @Override
    public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
                       int jobTimes, Map jobData) {
        try {
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .build();
            // 设置job参数
            if(jobData!= null && jobData.size()>0){
                jobDetail.getJobDataMap().putAll(jobData);
            }
            // 使用simpleTrigger规则
            Trigger trigger = null;
            if (jobTimes < 0) {
                trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                        .startNow().build();
            } else {
                trigger = TriggerBuilder
                        .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
                                .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                        .startNow().build();
            }
            log.info("jobDataMap: {}", jobDetail.getJobDataMap().getWrappedMap());
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("add job error!");
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass
     *            任务实现类
     * @param jobName
     *            任务名称(建议唯一)
     * @param jobGroupName
     *            任务组名
     * @param jobTime
     *            时间表达式 (如:0/5 * * * * ? )
     * @param jobData
     *            参数
     */
    @Override
    public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) {
        try {
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .build();
            // 设置job参数
            if(jobData!= null && jobData.size()>0){
                jobDetail.getJobDataMap().putAll(jobData);
            }
            // 定义调度触发规则
            // 使用cornTrigger规则
            // 触发器key
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                    .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build();
            // 把作业和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("jobDataMap: {}", jobDetail.getJobDataMap());
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("add job error!");
        }
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName
     * @param jobGroupName
     * @param jobTime
     */
    @Override
    public void updateJob(String jobName, String jobGroupName, String jobTime) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            log.info("new jobTime: {}", jobTime);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("update job error!");
        }
    }

    /**
     * 删除任务一个job
     *
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     */
    @Override
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("delete job error!");
        }
    }

    /**
     * 暂停一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    @Override
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("pause job error!");
        }
    }

    /**
     * 恢复一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    @Override
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("resume job error!");
        }
    }

    /**
     * 立即执行一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    @Override
    public void runAJobNow(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("run a job error!");
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> queryAllJob() {
        List<Map<String, Object>> jobList = null;
        try {
            GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            jobList = new ArrayList<Map<String, Object>>();
            for (JobKey jobKey : jobKeys) {
                log.info("maps: {}", scheduler.getJobDetail(jobKey).getJobDataMap().getWrappedMap());
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    map.put("description", "触发器:" + trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        map.put("jobTime", cronExpression);
                    }
                    jobList.add(map);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("query all jobs error!");
        }
        return jobList;
    }

    /**
     * 获取所有正在运行的job
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> queryRunJob() {
        List<Map<String, Object>> jobList = null;
        try {
            List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
            jobList = new ArrayList<Map<String, Object>>(executingJobs.size());
            for (JobExecutionContext executingJob : executingJobs) {
                Map<String, Object> map = new HashMap<String, Object>();
                JobDetail jobDetail = executingJob.getJobDetail();
                JobKey jobKey = jobDetail.getKey();
                Trigger trigger = executingJob.getTrigger();
                map.put("jobName", jobKey.getName());
                map.put("jobGroupName", jobKey.getGroup());
                map.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                map.put("jobStatus", triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    map.put("jobTime", cronExpression);
                }
                jobList.add(map);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            throw new BaseException("query run jobs error!");
        }
        return jobList;
    }

}

Quartz使用方式

应用启动时自动调用

  写一个配置类,使用@Bean注解进行配置实例化。

QuartzConfig配置类

package com.andya.selfcode.quartz;

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

/**
 * @author Andya
 * @create 2021/04/01
 */
@Configuration
@Service
public class QuartzConfig {

    @Bean
    public JobDetail scheduleJobDetail() {
        System.out.println("**************************************** scheduler job begin");
        JobDetail jobDetail = JobBuilder.newJob(SchedulerJob.class)
                .withIdentity("schedulerJob")
                .storeDurably()
                .build();
        System.out.println("**************************************** scheduler job end");
        return jobDetail;
    }

    @Bean
    public Trigger scheduleJobDetailTrigger() {
        Trigger trigger = TriggerBuilder
                .newTrigger()
                .forJob(scheduleJobDetail())
                .withIdentity("schedulerJob")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(0))
                .startNow()
                .build();
        System.out.println("schedulerJob trigger end");
        return trigger;
    }

SchedulerJob任务类增删

package com.andya.selfcode.quartz;

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * @author Andya
 * @create 2021/04/01
 */
@Slf4j
public class SchedulerJob extends QuartzJobBean {

    @Autowired
    QuartzService quartzService;

    @Value("${schedule.cron.withJob1}")
    private String cronTimeJob1;

    public String getCronTimeJob1() {
        return cronTimeJob1;
    }

    @Value("${schedule.cron.withJob2}")
    private String cronTimeJob2;

    public String getCronTimeJob1() {
        return cronTimeJob2;
    }

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try{
            //job1先删后增
            log.info("job1: delete scheduleWithJob1");
            quartzService.deleteJob("scheduleWithJob1", "scheduleWithJob1_Group1");

            log.info("job1: add scheduleWithJob1");
            quartzService.addJob(ScheduleWithJob1.class, "scheduleWithJob1",
                    "scheduleWithJob1_Group1", cronTimeJob1, null);

            //按小时定时的job先删后增
            log.info("job2: delete scheduleWithJob2");
            quartzService.deleteJob("scheduleWithJob2", "scheduleWithJob2_Group2");

            log.info("job2: add scheduleWithJob2");
            quartzService.addJob(ScheduleWithJob2.class, "scheduleWithJob2",
                    "scheduleWithJob2_Group2", cronTimeJob2, null);
        } catch (Exception e) {
            log.error("quartz service scheduler job failed!");
            e.printStackTrace();
        }
    }
}

具体Job任务类

ScheduleWithJob1类
package com.andya.selfcode.quartz;

import com.andya.selfcode.service.ScheduleJobService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;

/**
 * @author Andya
 * @create 2021/04/01
 */
@Slf4j
public class ScheduleWithJob1 extends QuartzJobBean {

    @Autowired
    ScheduleJobService scheduleJobService;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException{
        System.out.println("start schedule job1: " + LocalDateTime.now());
        try {
            scheduleJobService.scheduleWithJob1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ScheduleWithJob2类
package com.andya.selfcode.quartz;

import com.andya.selfcode.service.ScheduleJobService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;

/**
 * @author Andya
 * @create 2021/04/01
 */
public class ScheduleWithJob2 extends QuartzJobBean {

    @Autowired
    ScheduleJobService scheduleJobService;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            System.out.println("start schedule with job2: " + LocalDateTime.now());
            scheduleJobService.scheduleJob2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
ScheduleJobService接口类
package com.andya.selfcode.service;

/**
 * @author Andya
 * @create 2021/04/01
 */
public interface ScheduleJobService {

    /**
     * job1定时任务
     * @throws Exception
     */
    void scheduleJob1() throws Exception;

    /**
     * job2定时任务
     * @throws Exception
     */
    void scheduleJob2() throws Exception;
}

HTTP接口方式调用

  写一个controller层直接调用QuartzService接口类。

package com.andya.selfcode.quartz.controller;

import com.andya.selfcode.quartz.bean.UpdateJobBean;
import com.andya.selfcode.quartz.bean.JobXXXBean;
import com.andya.selfcode.quartz.exception.BadRequestException;
import com.andya.selfcode.quartz.service.QuartzService;
import com.andya.selfcode.quartz.service.jobs.Job1;
import io.swagger.annotations.*;
import org.quartz.JobDataMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @author Andya
 * @create 2021/04/01
 */
@RestController
@Api(value = "quartz增删改查相关API")
@RequestMapping(value = "/quartz")
public class YarnFlexibleCapacityExpansionController {

    @Autowired
    QuartzService quartzService;

    @ApiOperation(value = "使用quartz添加job")
    @RequestMapping(value = "/addJob/{jobUUID}", method = RequestMethod.POST)
    public void addQuartzJob(
            @ApiParam(name = "jobUUID") @PathVariable("jobUUID") String jobUUID,
            @ApiParam(name = "JobXXXBean") @RequestBody JobXXXBean jobXXXBean) {

        if (jobXXXBean.getOpenBean() != null) {
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("key01", jobXXXBean.getKey01());
            jobDataMap.put("key02", jobXXXBean.getKey02());
            jobDataMap.put("key03", jobXXXBean.getKey03());
            jobDataMap.put("jobTimeCron", jobXXXBean.getJobTimeCron());
            jobDataMap.put("key04", jobXXXBean.getKey04());
            quartzService.addJob(Job1.class,
                    jobUUID,
                    jobUUID,
                    jobXXXBean.getJobTimeCron(),
                    jobDataMap);
        } else {
            throw new BadRequestException("参数错误");
        }
    }


    @ApiOperation(value = "使用quartz查询所有job")
    @RequestMapping(value = "/queryAllJob", method = RequestMethod.GET)
    public List<Map<String, Object>> queryAllQuartzJob() {

        List<Map<String, Object>> list = quartzService.queryAllJob();
        return list;
    }


    @ApiOperation(value = "使用quartz查询所有运行job")
    @RequestMapping(value = "/queryRunJob", method = RequestMethod.GET)
    public List<Map<String, Object>> queryRunQuartzJob() {

        List<Map<String, Object>> list = quartzService.queryRunJob();
        return list;
    }

    @ApiOperation(value = "使用quartz删除job")
    @RequestMapping(value = "/deleteJob/{jobUUID}", method = RequestMethod.DELETE)
    public void deleteJob(
            @ApiParam(name = "jobUUID") @PathVariable("jobUUID") String jobUUID) {

        quartzService.deleteJob(jobUUID, jobUUID);
    }


    @ApiOperation(value = "使用quartz修改job的cron时间")
    @RequestMapping(value = "/updateJob/{jobUUID}", method = RequestMethod.PUT)
    public void deleteJob(
            @ApiParam(name = "jobUUID") @PathVariable("jobUUID") String jobUUID,
            @ApiParam(name = "jobCronTime") @RequestBody UpdateJobBean updateJobBean) {

        quartzService.updateJob(jobUUID, jobUUID, updateJobBean.getJobCronTime());

    }
}


/**
 * @author Andya
 * @create 2021/04/01
 */
@ApiModel(value = "更新job cron时间参数")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UpdateJobBean {
    @ApiModelProperty(value = "jobTime的cron表达式", example = "0 0 1 * * ?")
    String jobCronTime;

    public String getJobCronTime() {
        return jobCronTime;
    }

    public void setJobCronTime(String jobCronTime) {
        this.jobCronTime = jobCronTime;
    }
}

Quartz数据表脚本

quartz初始化数据表的sql脚本如下。

-- 1.1. qrtz_blob_triggers : 以Blob 类型存储的触发器。
-- 1.2. qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。
-- 1.3. qrtz_cron_triggers:存放cron类型的触发器。
-- 1.4. qrtz_fired_triggers:存放已触发的触发器。
-- 1.5. qrtz_job_details:存放一个jobDetail信息。
-- 1.6. qrtz_job_listeners:job监听器。
-- 1.7. qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。
-- 1.8. qrtz_paused_trigger_graps:存放暂停掉的触发器。
-- 1.9. qrtz_scheduler_state:调度器状态。
-- 1.10. qrtz_simple_triggers:简单触发器的信息。
-- 1.11. qrtz_trigger_listeners:触发器监听器。
-- 1.12. qrtz_triggers:触发器的基本信息。

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(190) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(190) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(190) NULL,
JOB_GROUP VARCHAR(190) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit;

参考

作业调度框架 Quartz 的基本使用 | 小决的专栏 

https://www.cnblogs.com/Andya/p/14609065.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值