SpringBoot Quartz架构及单体应用介绍

Quartz架构介绍

1.关于Quartz

  • QuartzOpenSymphony 开源组织在 Job scheduling 领域开源的一个作业调度框架项目,完全由 Java 编写,主要是为了实现在 Java - 应用程序中进行作业调度并提供了简单却强大的机制!
    Quartz 不仅可以单独使用,还可以与 J2EEJ2SE 应用程序相结合使用!同时,Quartz 允许程序开发人员根据时间的间隔来调度作业!
  • JDK 中调度器不同的是,Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联,一次可以调度几十个、上百个甚至上几万个复杂的程序!

2.Quartz架构

在这里插入图片描述
从图中可以看出,Quartz 框架主要包括如下几个部分:

  • SchedulerFactory:任务调度工厂,主要负责管理任务调度器
  • Scheduler:任务调度控制器,主要是负责任务调度
  • Job:任务接口,即被调度的任务
  • JobDetailJob 的任务描述类,job 执行时会依据此对象的信息反射实例化出 Job 的具体执行对象
  • Trigger:任务触发器,主要存放 Job 执行的时间策略。例如多久执行一次,什么时候执行,以什么频率执行等等
  • CalendarTrigger 扩展对象,可以排除或者包含某个指定的时间点(如排除法定节假日)
  • JobStore:存储作业和任务调度期间的状态

我们通过一个简单的例子帮助大家理解一下,示例如下

public class QuartzTest implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
    public static void main(String[] args) throws SchedulerException {
        // 创建一个Scheduler
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 启动Scheduler
        scheduler.start();
        // 新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
        JobDetail job = JobBuilder.newJob(QuartzTest.class)
                .usingJobData("jobData", "test")
                .withIdentity("myJob", "myJobGroup")
                .build();
        // 新建一个Trigger, 表示JobDetail的调度计划, 这里的cron表达式是 每1秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "myTriggerGroup")
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                .build();
        // 让scheduler开始调度这个job, 按trigger指定的计划
        scheduler.scheduleJob(job, trigger);
    }
}

输出效果如下

2020-11-09 21:38:40
2020-11-09 21:38:45
2020-11-09 21:38:50
2020-11-09 21:38:55
2020-11-09 21:39:00
2020-11-09 21:39:05
2020-11-09 21:39:10
... ...

在应用方面使用最多的就是JobTrigger

3.源码解读

3.1 Job

Job的源码比较简单,其内部就是一个包含执行方法void execute(JobExecutionContext context)的接口,开发者只需要实现接口来定义具体任务即可。

public interface Job {
    void execute(JobExecutionContext context) throws JobExecutionException;
}

JobExecutionContext类封装了获取上下文的各种信息,Job运行时的信息也保存在JobDataMap示例中。比如我想获取到在上文初始化时使用到的参数,可以通过如下方式获取。

public void execute(JobExecutionContext context) throws JobExecutionException {
    //从context中获取instName,groupName以及dataMap
    String jobName = context.getJobDetail().getKey().getName();
    String groupName = context.getJobDetail().getKey().getGroup();
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    //从dataMap中获取myDescription,myValue以及myArray
    String value = dataMap.getString("jobData");
    System.out.println("jobName:" + jobName + ",groupName:" + groupName + ",jobData:" + value);
}

3.2 Trigger

Trigger主要用于描述Job执行的时间触发规则,最常用的有SimpleTriggerCronTrigger两个实现类型.

  • SimpleTrigger:主要处理一些简单的调度规则,例如触发一次或者以固定时间间隔周期执行
  • CronTrigger:调度处理更加灵活,可以通过Cron表达式定义出各种复杂时间规则的调度方案,例如每早晨9:00执行,周一、周三、周五下午5:00执行等
3.2.1 SimpleTrigger

例如使用SimpleTrigger实现没2s执行一次任务,代码如下

public static void main(String[] args) throws SchedulerException {
    //1.创建一个Scheduler
    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    //2.启动Scheduler
    scheduler.start();
    //3.新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
    JobDetail job = JobBuilder.newJob(QuartzTest.class)
             .usingJobData("jobData", "test")
             .withIdentity("myJob", "myJobGroup")
             .build();
    //4.构建一个Trigger,指定Trigger名称和组,规定该Job立即执行,且两秒钟重复执行一次
    SimpleTrigger trigger = TriggerBuilder.newTrigger()
            .startNow() // 执行的时机,立即执行
            .withIdentity("myTrigger", "myTriggerGroup") // 不是必须的
            .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
            .build();
    //5.让scheduler开始调度这个job, 按trigger指定的计划
    scheduler.scheduleJob(job, trigger);
}

其中最关键的就是withSchedule()方法,通过SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()来构建一个简单的SimpleTrigger类型的任务调度规则,从而实现任务调度。

3.2.2 CronTrigger

使用CronTrigger实现每5s执行一次任务,代码如下

public static void main(String[] args) throws SchedulerException {
    //1.创建一个Scheduler
    Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    //2.启动Scheduler
    scheduler.start();
    //3.新建一个Job, 指定执行类是QuartzTest, 指定一个K/V类型的数据, 指定job的name和group
    JobDetail job = JobBuilder.newJob(QuartzTest.class)
             .usingJobData("jobData", "test")
             .withIdentity("myJob", "myJobGroup")
             .build();
    //4.构建一个Trigger,指定Trigger名称和组,规定该Job立即执行,且两秒钟重复执行一次
    Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("myTrigger", "myTriggerGroup")
            .startNow() // 立即执行
            .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
            .build();
    //5.让scheduler开始调度这个job, 按trigger指定的计划
    scheduler.scheduleJob(job, trigger);
}

相比于SimpleTrigger,在配置调度规则方面,使用cron表达式更加灵活。
可以参考:Cron表达式详解

3.3 监听器(选用)

quartz 除了提供能正常调度任务的功能之外,还提供了监听器功能。
所谓监听器,其实你可以把它理解为类似Spring Aop的功能,可以对全局或者局部实现监听。

监听器应用,在实际项目中并不常用,但是在某些业务场景下,可以发挥一定的作用,例如:你想在任务处理完成之后,去发送邮件或者发短信进行通知,但是你又不想改以前的代码,这个时候就可以在监听器里面完成改项任务。

quartz 监听器主要分三大类:

  • SchedulerListener:任务调度监听器
  • TriggerListener:任务触发监听器
  • JobListener:任务执行监听器
3.3.1 SchedulerListener

SchedulerListener监听器,主要对任务调度Scheduler生命周期中关键节点进行监听,它只能全局进行监听,示例如下

public class SimpleSchedulerListener implements SchedulerListener {

    @Override
    public void jobScheduled(Trigger trigger) {
        System.out.println("任务被部署时被执行");
    }

    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
        System.out.println("任务被卸载时被执行");
    }

    @Override
    public void triggerFinalized(Trigger trigger) {
        System.out.println("任务完成了它的使命,光荣退休时被执行");
    }

    @Override
    public void triggerPaused(TriggerKey triggerKey) {
        System.out.println(triggerKey + "(一个触发器)被暂停时被执行");
    }

    @Override
    public void triggersPaused(String triggerGroup) {
        System.out.println(triggerGroup + "所在组的全部触发器被停止时被执行");
    }

    @Override
    public void triggerResumed(TriggerKey triggerKey) {
        System.out.println(triggerKey + "(一个触发器)被恢复时被执行");
    }

    @Override
    public void triggersResumed(String triggerGroup) {
        System.out.println(triggerGroup + "所在组的全部触发器被回复时被执行");
    }

    @Override
    public void jobAdded(JobDetail jobDetail) {
        System.out.println("一个JobDetail被动态添加进来");
    }

    @Override
    public void jobDeleted(JobKey jobKey) {
        System.out.println(jobKey + "被删除时被执行");
    }

    @Override
    public void jobPaused(JobKey jobKey) {
        System.out.println(jobKey + "被暂停时被执行");
    }

    @Override
    public void jobsPaused(String jobGroup) {
        System.out.println(jobGroup + "(一组任务)被暂停时被执行");
    }

    @Override
    public void jobResumed(JobKey jobKey) {
        System.out.println(jobKey + "被恢复时被执行");
    }

    @Override
    public void jobsResumed(String jobGroup) {
        System.out.println(jobGroup + "(一组任务)被恢复时被执行");
    }

    @Override
    public void schedulerError(String msg, SchedulerException cause) {
        System.out.println("出现异常" + msg + "时被执行");
        cause.printStackTrace();
    }

    @Override
    public void schedulerInStandbyMode() {
        System.out.println("scheduler被设为standBy等候模式时被执行");
    }

    @Override
    public void schedulerStarted() {
        System.out.println("scheduler启动时被执行");
    }

    @Override
    public void schedulerStarting() {
        System.out.println("scheduler正在启动时被执行");
    }

    @Override
    public void schedulerShutdown() {
        System.out.println("scheduler关闭时被执行");
    }

    @Override
    public void schedulerShuttingdown() {
        System.out.println("scheduler正在关闭时被执行");
    }

    @Override
    public void schedulingDataCleared() {
        System.out.println("scheduler中所有数据包括jobs, triggers和calendars都被清空时被执行");
    }
}

需要在任务调度器启动前,将SimpleSchedulerListener注册到Scheduler容器中

...

//1.创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.添加SchedulerListener监听器
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());
//3.启动Scheduler
scheduler.start();

...
3.3.2 TriggerListener

TriggerListener与触发器Trigger相关的事件都会被监听,它既可以全局监听,也可以实现局部监听,所谓局部监听,就是对某个Trigger的名称或者组进行监听,简单示例如下:

public class SimpleTriggerListener implements TriggerListener {

    //Trigger监听器的名称
    @Override
    public String getName() {
        return "mySimpleTriggerListener";
    }

    //Trigger被激发 它关联的job即将被运行
    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        System.out.println("myTriggerListener.triggerFired()");
    }

    //Trigger被激发 它关联的job即将被运行, TriggerListener 给了一个选择去否决 Job 的执行,如果返回TRUE 那么任务job会被终止
    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
        System.out.println("myTriggerListener.vetoJobExecution()");
        return false;
    }

    //当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
    //那么有的触发器就有可能超时,错过这一轮的触发。
    @Override
    public void triggerMisfired(Trigger trigger) {
        System.out.println("myTriggerListener.triggerMisfired()");
    }

    //任务完成时触发
    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
        System.out.println("myTriggerListener.triggerComplete()");
    }
}

同样的也需要注册到Scheduler容器中。

...

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());
        
// 创建并注册一个局部的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("myTrigger", "myJobTrigger")));
        
// 创建并注册一个特定组的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), GroupMatcher.groupEquals("myTrigger"));
// 启动Scheduler
scheduler.start();

...
3.3.3 JobListener

JobListener与任务执行Job相关的事件都会被监听,和Trigger一样,既可以全局监听,也可以实现局部监听。示例如下:

public class SimpleJobListener implements JobListener {

    //job监听器名称
    @Override
    public String getName() {
        return "mySimpleJobListener";
    }

    //任务被调度前
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("simpleJobListener监听器,准备执行:"+context.getJobDetail().getKey());
    }

    //任务调度被拒了
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("simpleJobListener监听器,取消执行:"+context.getJobDetail().getKey());
    }

    //任务被调度后
    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        System.out.println("simpleJobListener监听器,执行结束:"+context.getJobDetail().getKey());
    }
}

同样注册到Scheduler容器中,代码如下

...

// 创建一个Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());

// 创建并注册一个指定任务的Job Listener
//scheduler.getListenerManager().addJobListener(new SimpleJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("myJob", "myJobGroup")));

// 将同一任务组的任务注册到监听器中
//scheduler.getListenerManager().addJobListener(new SimpleJobListener(), GroupMatcher.jobGroupEquals("myJobGroup"));

// 启动Scheduler
scheduler.start();

...
3.3.4 多个同时监听

如果想要注册多个监听,将其依次加入皆苦,示例如下:

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

//添加SchedulerListener监听器
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());

// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());

// 创建并注册一个全局的Job Listener
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());

// 启动Scheduler
scheduler.start();

单体应用(SpringBoot + Quartz)

1 引入Quartz包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2 实现的具体业务

public class TestTask implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("testTask:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

3 编写任务调度配置服务

@Configuration
public class TestTaskConfig {
    @Bean
    public JobDetail testQuartz() {
        return JobBuilder.newJob(TestTask.class)
                .usingJobData("jobData", "test") //数据
                .withIdentity("myJob", "myJobGroup") //任务名称及分组
                .storeDurably() //信息持久化,默认为false
                .build();
    }
    @Bean
    public Trigger testQuartzTrigger() {
        //5秒执行一次
        return TriggerBuilder.newTrigger()
                .forJob(testQuartz())
                .withIdentity("myTrigger", "myTriggerGroup") //触发器名称及分组
                .startNow() //立即执行
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                .build();
    }
}

注意:存在的问题,如果需要创建多个定时任务,我们就需要按照此流程进行多次配置,这种静态配置多了,就会给后维护带来不便,比如,我任务的执行周期发生变动,这个时候就需要修改代码,但是我们又不想修改代码,那怎么办呢?

4 动态配置定时任务

上边我们说到,静态配置一旦发生需求变动,我们就必须修改代码,那么为了更好的维护代码,减轻我们维护人员的工作压力。我们就需要 利用数据库来实现动态的定时任务。
从以上的代码阅读中不难看出,定时任务存在三个核心变量,其他的方法都是可以封装的。

  • 任务名称:例如myJob
  • 任务执行类:例如TestTask.class
  • 任务调度时间:例如0/5 * * * * ?
    基于此,我们可以创建一个定时任务的实体类,用于保存定时任务相关的信息到数据库中,然后编写一个定时任务工具库,用于创建、更新、删除、暂停任务操作。通过restful接口操作将任务存入数据库并管理定时任务。

4.1 引入包

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

4.2 添加数据库配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/netty?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

4.3 创建任务配置表

CREATE TABLE `tb_job_task` (
  `id` varchar(50)  NOT NULL COMMENT '任务ID',
  `job_name` varchar(100)  DEFAULT NULL COMMENT '任务名称',
  `job_class` varchar(200)  DEFAULT NULL COMMENT '任务执行类',
  `cron_expression` varchar(50)  DEFAULT NULL COMMENT '任务调度时间表达式',
  `status` int(4) DEFAULT NULL COMMENT '任务状态,0:启动,1:暂停,2:停用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

4.4 编写任务实体类

public class QuartzBean {
    private String id;
    private String jobName;
    private String jobClass;
    private String cronExpression;
    private Integer status;

    //get、set...
}

4.5 编写任务操作工具类

public class QuartzUtils {

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

    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     * @param scheduler   调度器
     * @param quartzBean  定时任务信息类
     */
    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) {
            log.error("定时任务类路径出错:请输入类的绝对路径", e);
        } catch (SchedulerException e) {
            log.error("创建定时任务出错", e);
        }
    }

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

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

    /**
     * 根据任务名称立即运行一次定时任务
     * @param scheduler     调度器
     * @param jobName       定时任务名称
     */
    public static void runOnce(Scheduler scheduler, String jobName){
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            log.error("运行定时任务出错", e);
        }
    }

    /**
     * 更新定时任务
     * @param scheduler   调度器
     * @param quartzBean  定时任务信息类
     */
    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) {
            log.error("更新定时任务出错", e);
        }
    }

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

4.6 编写controller服务

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

    private static final Logger log = LoggerFactory.getLogger(QuartzController.class);

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private QuartzBeanRepository repository;

    /**
     * 创建任务
     */
    @RequestMapping("/createJob")
    public HttpStatus createJob(@RequestBody  QuartzBean quartzBean)  {
        log.info("=========开始创建任务=========");
        QuartzUtils.createScheduleJob(scheduler,quartzBean);
        repository.save(quartzBean.setId(UUID.randomUUID().toString()).setStatus(0));
        log.info("=========创建任务成功,信息:{}", JSON.toJSONString(quartzBean));
        return HttpStatus.OK;
    }

    /**
     * 暂停任务
     */
    @RequestMapping("/pauseJob")
    public HttpStatus pauseJob(@RequestBody QuartzBean quartzBean) {
        log.info("=========开始暂停任务,请求参数:{}=========", JSON.toJSONString(quartzBean));
        QuartzUtils.pauseScheduleJob(scheduler, quartzBean.getJobName());
        repository.updateState(quartzBean.getId(), 1);
        log.info("=========暂停任务成功=========");
        return HttpStatus.OK;
    }

    /**
     * 立即运行一次定时任务
     */
    @RequestMapping("/runOnce")
    public HttpStatus runOnce(@RequestBody QuartzBean quartzBean) {
        log.info("=========立即运行一次定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
        QuartzUtils.runOnce(scheduler,quartzBean.getJobName());
        log.info("=========立即运行一次定时任务成功=========");
        return HttpStatus.OK;
    }

    /**
     * 恢复定时任务
     */
    @RequestMapping("/resume")
    public HttpStatus resume(@RequestBody QuartzBean quartzBean) {
        log.info("=========恢复定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
        QuartzUtils.resumeScheduleJob(scheduler,quartzBean.getJobName());
        repository.updateState(quartzBean.getId(), 0);
        log.info("=========恢复定时任务成功=========");
        return HttpStatus.OK;
    }

    /**
     * 更新定时任务
     */
    @RequestMapping("/update")
    public HttpStatus update(@RequestBody QuartzBean quartzBean)  {
        log.info("=========更新定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
        QuartzUtils.updateScheduleJob(scheduler,quartzBean);
        repository.updateCron(quartzBean.getId(), quartzBean.getCronExpression());
        log.info("=========更新定时任务成功=========");
        return HttpStatus.OK;
    }

    /**
     * 删除定时任务
     */
    @RequestMapping("/delete")
    public HttpStatus delete(@RequestBody QuartzBean quartzBean)  {
        log.info("=========删除定时任务,请求参数:{}", JSON.toJSONString(quartzBean));
        QuartzUtils.deleteScheduleJob(scheduler,quartzBean.getJobName());
        repository.updateState(quartzBean.getId(), 2);
        log.info("=========删除定时任务成功=========");
        return HttpStatus.OK;
    }

}

5 服务重启补偿

在应用程序正常运行的时候,虽然没问题,但是当我们重启服务的时候,这个时候内存的里面的定时任务其实全部都被销毁,因此在应用程序启动的时候,还需要将正常的任务重新加入到服务中。

@Component
public class TaskConfigApplicationRunner implements ApplicationRunner {

    @Autowired
    private QuartzBeanRepository repository;

    @Autowired
    private Scheduler scheduler;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<QuartzBean> list = repository.findAll();
        if(!CollectionUtils.isEmpty(list)){
            for (QuartzBean quartzBean : list) {
                //加载启动类型的定时任务
                if(quartzBean.getStatus().intValue() == 0){
                    QuartzUtils.createScheduleJob(scheduler,quartzBean);
                }
                //加载暂停类型的定时任务
                if(quartzBean.getStatus().intValue() == 1){
                    QuartzUtils.createScheduleJob(scheduler,quartzBean);
                    QuartzUtils.pauseScheduleJob(scheduler, quartzBean.getJobName());
                }
            }
        }
    }
}

6 添加监听器(选用)

@Configuration
public class TestTaskConfig {

 	@Primary
    @Bean
    public Scheduler initScheduler() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //添加SchedulerListener监听器
        scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());

        // 创建并注册一个全局的Trigger Listener
        scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener(), EverythingMatcher.allTriggers());

        // 创建并注册一个全局的Job Listener
        scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());
        scheduler.start();
        return scheduler;
    }

}

注意quartz 任务暂停之后再次启动时,会立即执行一次,在更新之后也会立即执行一次任务调度!

总结

本文主要围绕 quartz 的架构以及Springboot + quartz项目整合和应用做了初步的介绍,quartz 已经有对应的任务表,不需要手动创建,只需要配置quartz.properties文件即可!quartz官方提供了对应任务表,主要用于分布式架构下的任务处理,如果只是单体应用,可以参考本文中创建一张单表来存储任务,实现简单。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序小达人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值