定时任务JOB框架之Quartz (二) Quartz的进阶篇-多任务JOB

一、前言

《定时任务JOB框架之Quartz (一) Quartz入门与快速Demo搭建》文章中创建的工程只有1个JOB在执行1个定时任务,在实际的应用场景中一般的项目少则十几个定时任务,多的几十上百个不等。实际应用中不可能把所有定时任务都写到一个JOB中,那样会造成:

1、一个定时任务的故障会导致后面的定时任务无法继续执行

2、代码也不好维护,太臃肿了

3、单个JOB执行一次的耗时太长了

所以我们需要N个JOB,每一个JOB执行各自的定时任务,本文中举例2个JOB,1个JOB处理订单模块(OrderJob)的自动任务,1个JOB处理物流模块(DeliveryJob)的自动任务。在创建工程前,首先了解下Quartz几个重要的接口,如下:

Quartz的4个重要接口:

  • Scheduler - 用来和调度器交互的API
  • Trigger - 定义了Job运行的调度时间表
  • JobDetail - 用来定义任务实例
  • Job - 被调度的任务体

继承自Trigger接口的还有2个重要的接口:

  • SimpleTrigger - 简单的调度时间表(很少用)
  • CronTrigger - Cron表达式的调度时间表

二、创建Spring Boot工程

三、创建OrderJob和DeliveryJob类

3.1 OrderJob类

package com.ljhua.quartz2;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OrderJob implements Job {
    Logger log = LoggerFactory.getLogger(OrderJob.class);
    private int count = 0;

    @Override
    public void execute(JobExecutionContext context) {
        int curJob = count++;
        JobKey jobKey = context.getJobDetail().getKey();

        log.info(jobKey + "-处理逻辑-start:*** " + curJob);
        log.info(jobKey + "-处理逻辑-end: *** " + curJob);
    }
}

3.2 DeliveryJob类

package com.ljhua.quartz2;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DeliveryJob implements Job {
    Logger log = LoggerFactory.getLogger(DeliveryJob.class);
    private int count = 0;

    @Override
    public void execute(JobExecutionContext context) {
        int curJob = count++;
        JobKey jobKey = context.getJobDetail().getKey();

        log.info(jobKey + "-处理逻辑-start:### " + curJob);
        log.info(jobKey + "-处理逻辑-end: ### " + curJob);
    }
}

可以看到OrderJob和DeliveryJob没有太大的区别,就是进入定时任务后输出日志

四、创建QuartzConfiguration类

package com.ljhua.quartz2;

import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class QuartzConfiguration {

    @Bean(name = "orderDetail")
    public JobDetailFactoryBean orderDetailBean() {
        JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();

        //指定OrderJob为定时任务
        jobDetail.setJobClass(OrderJob.class);

        //JobKey由group.name组成
        jobDetail.setGroup("group1");
        jobDetail.setName("job1");

        return jobDetail;
    }

    @Bean(name = "deliveryDetail")
    public JobDetailFactoryBean deliveryDetailBean() {
        JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();

        //指定DeliveryJob为定时任务
        jobDetail.setJobClass(DeliveryJob.class);

        //JobKey由group.name组成
        jobDetail.setGroup("group1");
        jobDetail.setName("job2");

        return jobDetail;
    }

    @Bean(name = "orderTrigger")
    public CronTriggerFactoryBean orderTrigger(JobDetail orderDetail) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

        tigger.setJobDetail(orderDetail);

        //cron表达式,每1分钟执行一次
        tigger.setCronExpression("0/10 * * * * ?");

        tigger.setGroup("group1");
        tigger.setName("trigger1");

        return tigger;
    }

    @Bean(name = "deliveryTrigger")
    public CronTriggerFactoryBean deliveryTrigger(JobDetail deliveryDetail) {
        CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

        tigger.setJobDetail(deliveryDetail);

        //cron表达式,每1分钟执行一次
        tigger.setCronExpression("0/10 * * * * ?");

        tigger.setGroup("group1");
        tigger.setName("trigger2");

        return tigger;
    }

    @Bean(name = "scheduler")
    public SchedulerFactoryBean schedulerFactory(Trigger orderTrigger, Trigger deliveryTrigger) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();

        // 延时启动,应用启动1秒后
        factoryBean.setStartupDelay(1);

        // 注册触发器
        factoryBean.setTriggers(orderTrigger, deliveryTrigger);
        return factoryBean;
    }

}

执行工程,运行结果如下:

五、与《定时任务JOB框架之Quartz (一) Quartz入门与快速Demo搭建》文章中的区别点

5.1、JOB的区别

上一篇文章中HelloJob和本文中的OrderJob、DeliveryJob的区别

上一篇文章的JOB是一个普通类,本文中的JOB是继承自org.quartz.Job接口的,上一篇文章的curJob变量每执行一次输出值都是不断加1的,本文中的curJob变量每次输出都是0,可以看出本文中的JOB实例是每调用一次就释放了的,如果需要保持状态,可以用static定义变量,如private static int count = 0

5.2 MethodInvokingJobDetailFactoryBean和JobDetailFactoryBean的区别

上一篇文章中MethodInvokingJobDetailFactoryBean和本文中的JobDetailFactoryBean的区别

MethodInvokingJobDetailFactoryBean没有setJobClass方法,只能用setTargetObject和setTargetMethod来绑定到JOB及JOB中的方法,MethodInvokingJobDetailFactoryBean有setConcurrent方法可以设置JOB是否是并发模式执行

JobDetailFactoryBean可以用setJobClass方法绑定到JOB类就可以,不需求指定JOB中的方法,还可以用setJobDataMap来设置,但是没有setConcurrent方法,JobDetailFactoryBean创建的JobDetail默认是并发模式执行的,那要非并发执行怎么办?是在JOB类不是在JobDetail中设置,即在OrderJob和DeliveryJob类中加上@DisallowConcurrentExecution注解就可以了

六、构建器

上面的QuartzConfiguration类看起来就和以前Spring中的项目一样,虽然没有用XML文件来配置一个一个的Bean,但是每个Bean都要用四五行java代码来配置。如果有几十、几百个定时任务非常不好维护,有2种方式比较好管理,一种是用构建器方式,一种是从数据库中读取JobDetail和Trigger的配置数据,然后用循环语句调用构建器来构建。本文中主要演示构建器方式,这种能掌握了,复杂的也就掌握了。

Quartz还有2个重要的类,用于构建Trigger和JobDetail:

  • TriggerBuilder - Trigger构建器
  • JobBuilder - jobDetail构建器

6.1 用org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration生成的Scheduler调度器

用这种方式创建的调度器的Bean名是quartzScheduler,除非在quartz.properties配置文件指定了名称,用构建器方式来优化QuartzConfiguration类,新QuartzConfiguration类的代码如下:

package com.ljhua.quartz2;

import org.quartz.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

@Configuration
@AutoConfigureAfter({SchedulerFactoryBean.class})
public class QuartzConfiguration {

    public QuartzConfiguration(SchedulerFactoryBean quartzScheduler) throws SchedulerException {
        Scheduler sched = quartzScheduler.getScheduler();

        JobDetail job1 = JobBuilder.newJob(OrderJob.class).withIdentity("job1", "group1").build();
        CronTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0/10 * * * * ?")).build();
        sched.scheduleJob(job1, trigger1);

        JobDetail job2 = JobBuilder.newJob(DeliveryJob.class).withIdentity("job2", "group1").build();
        CronTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").withSchedule(cronSchedule("0/10 * * * * ?")).build();
        sched.scheduleJob(job2, trigger2);

        sched.startDelayed(1);
    }

}

是不是代码少了很多?不只是代码少了,这种方式还便于扩展成从数据库中读取JobDetail和Trigger的配置数据,然后用循环语句调用构建器来构建,这对于定时任务很多的项目很有必要。

新QuartzConfiguration类的实现思路是,我自己写的代码里不创建Scheduler调度器,调用spring-boot-autoconfigure-2.2.4.RELEASE.jar包中自动生成的Scheduler调度器,实现流程如下:

1、通过@AutoConfigureAfter({SchedulerFactoryBean.class})注解,让QuartzConfiguration类的构造函数等org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration类中的quartzScheduler()方法执行成功后才执行,QuartzAutoConfiguration类中的quartzScheduler()方法执行后就会生成一个名为quartzScheduler的调度器。

2、然后在我自己写的QuartzConfiguration类的构造函数中用TriggerBuilder和JobBuilder构造器生成Trigger和JobDetail对象,再把对象往quartzScheduler调度器中添加就可以了。

6.2 在自己写的java类里生成Scheduler调度器

可以不用QuartzAutoConfiguration创建的调度器,自己new个调度器出来,新QuartzConfiguration类的代码如下:

package com.ljhua.quartz2;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

@Configuration
public class Quartz3Configuration {
    Logger log = LoggerFactory.getLogger(Quartz3Configuration.class);

    @Bean(name = "scheduler")
    public SchedulerFactoryBean schedulerFactory() {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();

        JobDetail job1 = JobBuilder.newJob(OrderJob.class).withIdentity("job1", "group1").storeDurably().build();
        CronTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1").forJob(job1).withSchedule(cronSchedule("0/10 * * * * ?")).build();

        JobDetail job2 = JobBuilder.newJob(DeliveryJob.class).withIdentity("job2", "group1").storeDurably().build();
        CronTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1").forJob(job2).withSchedule(cronSchedule("0/10 * * * * ?")).build();

        factoryBean.setJobDetails(job1, job2);
        factoryBean.setTriggers(trigger1, trigger2);

        return factoryBean;
    }
}

6.2与6.1的不同点:

1、这里要new SchedulerFactoryBean()

2、JobBuilder构建JobDetail时要加上storeDurably(),否则会报错误信息Jobs added with no trigger must be durable

3、TriggerBuilder.newTrigger()要加上forJob(),把Trigger指定到对应的JobDetail

4、要用factoryBean.setJobDetails()把JobDetail加到调度器中,否则会报错误信息The job (group1.job1) referenced by the trigger does not exist.

发布了5 篇原创文章 · 获赞 0 · 访问量 158

猜你喜欢

转载自blog.csdn.net/anron/article/details/104456454