Quartz高可用定时任务的使用

565 阅读3分钟

一、介绍

   Quartz出色的定时任务调度框架,事实上Java语言关于任务调度方面的API标准。

二、基础使用

2.1 快速使用

   快速上手可帮助用户快速体验Quartz定时调度框架,无需关注配置细节。

2.1.1 新建项目

   新建标准的SpringBoot项目,并导入主要依赖。

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

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

2.1.2 新建Job

测试代码

public class DemoJob implements Job {
    @Override
    public void execute(JobExecutionContext context) {
        System.out.println("简单执行一下,不获取参数,不输出结果" + LocalDateTime.now());
    }
}

2.1.3 新建配置类

@Configuration
public class QuartzConfig {
    
    public static final String JOB_NAME = "syncUserJobDetail";
    public static final String TRIGGER_NAME = "syncUserJobTrigger";
    
    /**
     * 绑定任务详情
     */
    @Bean(JOB_NAME)
    public JobDetail jobDetail() {
        return JobBuilder.newJob(DemoJob.class).withIdentity(JOB_NAME).storeDurably().build();
    }
    
    /**
     * 绑定触发器
     */
    @Bean(TRIGGER_NAME)
    public Trigger trigger() {
        //每隔5秒执行一次
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
            .cronSchedule("0/5 * * * * ?");
        //创建触发器
        return TriggerBuilder.newTrigger().forJob(jobDetail()).withIdentity(TRIGGER_NAME)
            .withSchedule(cronScheduleBuilder).build();
    }
}

完整代码仓库项目地址:

git clone https://gitee.com/java-spring-demo/quartz-demo.git

2.2 配置详解

2.2.1 调度器配置

调度器一般除了修改名称外,使用默认配置即可。

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.instanceid: AUTO 
org.quartz.scheduler.rmi.export: false 
org.quartz.scheduler.rmi.proxy: false 
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

2.2.2 线程池配置

任务的调度是通过线程池来实现的,因此需要配置线程池。如果被调度的任务

# 线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求) 
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool 
# 指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount: 10 
# 设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5) 
org.quartz.threadPool.threadPriority: 5 
# 设置SimpleThreadPool的一些属性 
# 设置是否为守护线程 
# org.quartz.threadpool.makethreadsdaemons = false 
# org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true 
# org.quartz.threadpool.threadsinheritgroupofinitializingthread=false 
# 线程前缀默认值是:[Scheduler Name]_Worker 
# org.quartz.threadpool.threadnameprefix=swhJobThead;

2.2.3 持久化配置

   根据定时任务的需要,是否需要持久化任务到磁盘上,具体判别标准如下:定时任务是否需要高可用。如果无高可用需求,简单单节点运行,无需持久化。

2.2.3.1 默认配置

默认配置不需要持久化,相关信息存储在内存中,读写速度快。

# 信息保存时间 默认值60秒 
org.quartz.jobStore.misfireThreshold: 60000 
# 保存job和Trigger的状态信息到内存中的类 
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

2.2.3.2 持久化到DB

   Quartz支持将任务及任务调度信息持久化到数据库,实现了定时任务的高可用。当定时任务多开时,通过数据库读取任务信息,防止同一任务重复执行。

spring: 
    quartz: 
        properties: 
            org.quartz.jobStore.misfireThreshold: 5000
            org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
            org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate 
            org.quartz.jobStore.useProperties: true
            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-demo 
            org.quartz.dataSource.qzDS.user: root 
            org.quartz.dataSource.qzDS.password: 123456
            org.quartz.jobStore.isClustered: true

三、理论

3.1 并发执行

   Quartz的并发执行的含义是当任务的调度间隔小于任务的执行耗时时,是否执行。

   一般来说在设置定时任务时尽量避免此种情况发生,然后任务在实际执行过程中,可能会偏离正常执行耗时,此时需要从机制上保证同一任务,只有在上一次调度完成后,方进行下一次调度。

   通过注解DisallowConcurrentExecution禁止任务并发执行。

@DisallowConcurrentExecution 
public class DemoJob implements Job { 
    @Override 
    public void execute(JobExecutionContext context) { 
    } 
}

3.2 幂等性

   在编写定时任务时,一般尽量满足幂等性原则:任务执行一次或者执行多次的效果是一致的。比如编写定时任务,将商品表转换成Redis版的BitMap,此时任务满足幂等性原则。

   幂等性有助于消除任务失败重试带来的负面影响。