springboot quartz 分布式定时任务

即使一手托帝城 背负天渊 我安澜也一样无敌于世间

俞陀救我!!!

quartz 简介及核心组件 :

quartz 的调度策略是以数据库资源为中介的一种异步策略,各个节点的调度器都遵守基于数据库锁的操作规则从而保证了任务执行的唯一性具体体现在 quartz 中的 quartz_locks 表,quartz 采用了悲观锁的方式对 triggers 行行加锁,从而保证了任务同步的正确性。当某个节点的某个线程在任务执行时间点获取到该 triggers 的锁时,该任务的这次执行就会发生在该节点上,同时当其它节点在该任务执行时间点触发该任务时发现锁被占用,则会忽略此次执行。其中一个任务可被两个节点执行,即这次节点一执行,下一次节点二执行(前提是获取到锁)

  • Job 定时任务的具体内容
  • JobDetail 可被调度容器调度的调度程序,其中 Job 是必要组成部分
  • Trigger 定时任务触发器
  • Secheduler 调度容器,主要组成部分为 JobDetail 和 Trigger(两者一一对应),容器内可注册多对调度对象

quartz 相关表 :

建表语句在引入的依赖包中可找到,org.quartz.impl.jdbcjobstore 下的 tables_mysql_innodb.sql(适用于 mysq,目录下有支持几乎所有主流数据库的 sql 文件,不同版本的 sql 文件位置可能不一样)

  • qrtz_blob_triggers trigger 作为 blob 类型存储,(用于用户定制的 trigger 而 JobStore 不知道的类型的存储)
  • qrtz_calendars calendars 作为 blob 类型存储
  • qrtz_cron_triggers 存储程序中已配置的 cron 信息,包括 cron 表达式、trigger 名称和时区等信息
  • qrtz_fired_triggers 存储已触发的 trigger 和对应 job 相关信息(是吗?我觉得不像)
  • qrtz_job_details 存储已配置的 job 信息
  • qrtz_locks 存储程序对任务操作时锁的信息
  • qrtz_paused_trigger_grps 存储已暂停的 trigger 信息
  • qrtz_scheduler_state 存储 scheduler 的相关状态信息
  • qrtz_simple_triggers 存储 trigger 信息,包括重复次数、间隔和已触发次数等
  • qrtz_simprop_triggers 存储 CalendarIntervalTrigger 和 DailyTimeIntervalTrigger
  • qrtz_triggers 存储已配置的 trigger 信息

注: 详细情况参考

springboot 整合 quartz 依赖 :

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

application.yml:

spring:
  quartz:
    auto-startup: true   # 服务启动后自动启动任务
    job-store-type: jdbc   # job 存储类型 默认为 memory 只有设置为 jdbc 时才可持久化到数据库
    jdbc:
      initialize-schema: always   # 初始化数据库 always 表示服务每次启动都会在配置的数据库中创建 quartz 对应的表

quartz.properties :

# quartz.properties 文件为 quartz 配置文件,若不配置,则使用 jar 包自带的默认文件
# 默认配置文件位于 jar 包 org.quartz 目录下
#
############################# 调度容器配置 #############################
# 调度容器名称 集群中所有示例名称都必须相同(非必须 默认 QuartzScheduler)
org.quartz.scheduler.instanceName = springboot-quartz
# 调度容器 id(非必须 默认 NON_CLUSTERED)
org.quartz.scheduler.instanceId = AUTO
# quartz-scheduler 出口本身通过 rmi 作为服务器(非必须 默认未 false)
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
# 如果想使用 quartz 在执行 job 前使用 UserTransaction,则设置为 true(非必须 默认为 false)
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

############################# 定时任务线程池配置 #############################
# 定时任务线程池 SimpleThreadPool 几乎满足所有用户需求(必须 默认 null)
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 线程池线程数(必须 默认 -1)
org.quartz.threadPool.threadCount = 2
# 线程池线程优先级(非必须 默认 5 最小 1最大 10)
org.quartz.threadPool.threadPriority = 5
# 是否未守护线程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

############################# 调度器配置 #############################
# 调度器相关信息存储(非必须 默认 org.quartz.simpl.RAMJobStore)
# org.quartz.simpl.RAMJobStore: 存储在 RAM 中 org.quartz.impl.jdbcjobstore.JobStoreTX: 存储在数据库中
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 当存储方式为 JobStoreTX 时持久化方式为 org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 设置 JDBCJobStore 中的 JobDataMaps 以键值对形式存储
org.quartz.jobStore.useProperties = true
# 是否集群、负载均衡、容错(非必须 默认 false)
org.quartz.jobStore.isClustered = true
# quartz 数据源(数据源命名随意,但需与下面持久化配置中保持一致)
org.quartz.jobStore.dataSource = quartzDataSource
# quartz 相关表名前缀
org.quartz.jobStore.tablePrefix = QRTZ_

############################# 持久化配置 #############################
# quartz 持久化连接池(非必须 默认 cp30)
# quartz 本身默认支持 cp30 和 hikaricp 若想使用 druid 则值为 com.cbw.quartz02.util.DruidConnectionProvider
org.quartz.dataSource.quartzDataSource.provider = hikaricp
# 数据库相关配置
org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://ip:3306/springboot-quartz?useUnicode=true&characterEncoding=utf8&useSSL=false&&serverTimezone=UTC
org.quartz.dataSource.quartzDataSource.user = root
org.quartz.dataSource.quartzDataSource.password = ******
# 最大连接数(听说当数据库连接池为 druid 时为 maxConnection 为 cp30 和 hikaricp 时为 maxConnections 未测试)
org.quartz.dataSource.quartzDataSource.maxConnections = 10

创建定时任务 :

// 创建两个定时任务
public class OneJobBean extends QuartzJobBean {
    private static final Logger logger = LogManager.getLogger(OneJobBean.class);
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        logger.info("定时任务一 threadName = " + Thread.currentThread().getName() +
                " time = " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }
}

public class TwoJobBean extends QuartzJobBean {
    private static final Logger logger = LogManager.getLogger(OneJobBean.class);
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        logger.info("定时任务二 threadName = " + Thread.currentThread().getName() +
                " time = " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }
}

QuartzConfig :

@Configuration
@EnableScheduling
public class QuartzConfig {

    /**
     * 定时任务一 job detail
     * @return
     */
    @Bean
    public JobDetailFactoryBean oneJobDetail() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        // 定时任务具体业务类
        jobDetailFactoryBean.setJobClass(OneJobBean.class);
        // 定时任务名称 可从配置文件加载
        jobDetailFactoryBean.setName("one job");
        return jobDetailFactoryBean;
    }

    /**
     * 定时任务一 cron trigger
     * @return
     */
    @Bean
    public CronTriggerFactoryBean oneCronTrigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        // 将 job detail 设置到触发器
        cronTriggerFactoryBean.setJobDetail(oneJobDetail().getObject());
        // 定时任务执行时间 可从配置文件加载
        cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?");
        return cronTriggerFactoryBean;
    }

    /**
     * 定时任务二 job detail
     * @return
     */
    @Bean
    public JobDetailFactoryBean twoJobDetail() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(TwoJobBean.class);
        jobDetailFactoryBean.setName("two job");
        return jobDetailFactoryBean;
    }

    /**
     * 定时任务二 cron trigger
     * @return
     */
    @Bean
    public CronTriggerFactoryBean twoCronTrigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(twoJobDetail().getObject());
        cronTriggerFactoryBean.setCronExpression("0/10 * * * * ?");
        return cronTriggerFactoryBean;
    }

    /**
     * 定时任务调度容器工厂
     * @return
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        // quartz 配置文件 若不设置则使用默认配置文件
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/config/quartz.properties"));
        // 将定时任务设置到工厂
        schedulerFactoryBean.setTriggers(oneCronTrigger().getObject(), twoCronTrigger().getObject());
        // 启动后定时任务执行延迟时间 单位 s
        schedulerFactoryBean.setStartupDelay(10);
        return schedulerFactoryBean;
    }
}

长路漫漫 该当如何

@XGLLHZ-无条件.mp3