SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz

news2024/11/26 9:12:40

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz

  • 简介
  • 适用场景
  • Quartz核心概念
  • Quartz 存储方式
  • Quartz 版本类型
  • 引入相关依赖
  • 方式一:内存方式(MEMORY)存储实现定时任务
    • 1. 定义任务类
    • 2. 定义任务描述及创建任务触发器
    • 3. Quartz的yml配置(按需配置)
  • 方式二:数据库(JDBC)方式存储实现定时任务
    • 1. 创建相关表
    • 2. 引入mysql相关依赖
    • 3. Quartz的yml配置
    • 4. 配置数据源
    • 启动查看

简介

Quartz是一个完全由Java开发的开源任务日程管理系统(或称为作业调度框架),它能够集成于任何Java应用,小到独立的应用,大至电子商务系统。
Quartz提供了丰富的调度功能和灵活的配置选项,帮助开发者实现复杂的任务调度和定时任务功能。

适用场景

Quartz广泛应用于各种企业级应用系统中,如电子商务、金融、物流等领域。
无论是简单的定时任务(如定时发送邮件、定时清理临时文件等),还是复杂的分布式任务(如分布式定时任务调度、任务依赖关系管理等),Quartz都能够胜任。

Quartz核心概念

  • Job(作业)
      Quartz中的任务,是一个需要被调度执行的接口,任务类需要实现该接口,并在execute方法中编写具体的业务逻辑。
  • JobDetail
      JobDetail用来绑定Job,并为Job实例提供许多属性,如名称、组名、Job类名以及JobDataMap等。JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。
  • Trigger(触发器)
      Trigger是Quartz的触发器,它描述了触发Job执行的时间触发规则。Trigger主要包含两种类型:SimpleTrigger和CronTrigger。
  • Scheduler(调度器)
      Scheduler是Quartz的调度器,它负责基于Trigger设定的时间执行Job。Scheduler是一个容器,它装载着任务和触发器。

Quartz 存储方式

Quartz 存储方式有两种:MEMORY 和 JDBC。

  • MEMORY(或RAMJobStore):这是Quartz的默认存储方式,它将任务调度的运行信息保存在内存中。这种方式提供了最佳的性能,因为内存中数据访问最快。然而,它的不足之处在于缺乏数据的持久性,当程序中途停止或系统崩溃时,所有运行的信息都会丢失。
  • JDBC:这种存储方式允许Quartz通过JDBC将任务调度的运行信息保存到数据库中。使用数据库保存任务调度信息后,即使系统崩溃后重新启动,任务的调度信息也将得到恢复。因此,JDBC存储方式提供了数据的持久性。

Quartz 版本类型

Quartz 版本类型有两种:单机版 和 集群版

  • 单机版:这是Quartz的默认版本类型 ,Quartz运行在一个单一的Java虚拟机(JVM)实例中。它通常使用MEMORY存储方式,因为这种方式配置容易且运行速度快。然而,单机版Quartz存在单点故障的风险,如果应用程序所在的服务器出现故障,任务调度将会停止。

  • 集群版:集群版Quartz可以在多个JVM实例中运行,实现高可用性和负载均衡。在集群版中,通常使用JDBC存储方式,以便在多个节点之间共享任务调度的数据。这样,即使其中一个节点出现故障,其他节点仍然可以继续工作,从而保证了任务调度的连续性和可靠性。

引入相关依赖

<!--quartz定时任务-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

方式一:内存方式(MEMORY)存储实现定时任务

1. 定义任务类

可以通过实现 Job 接口来定义任务,也可以通过继承 QuartzJobBean 这个抽象类来定义任务,其实 QuartzJobBean 本身也实现了 Job 接口,其本质都是实现 Job 接口来定义任务。

我这边定义了两个任务,用于后续来说明一下关于调度器Scheduler绑定的不同方式
Scheduler绑定有两种方式,一种是使用bena的自动配置,一种是Scheduler手动配置。

FirstJob 类

package com.example.springbootfull.quartztest;

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
public class FirstJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        log.info("手动-FirstJob, 当前的时间: " + now);
    }
}

SecondJob 类

package com.example.springbootfull.quartztest;

import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
public class SecondJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
        log.info("自动-SecondJob, 当前的时间: " + now);
    }
}

2. 定义任务描述及创建任务触发器

方式一:Scheduler手动配置

这个例子使用的是触发器类型为Cron

package com.example.springbootfull.quartztest.config;

import com.example.springbootfull.quartztest.FirstJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class JobInit implements ApplicationRunner {

    private static final String ID = "MANUAL";

    @Autowired
    private Scheduler scheduler;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 配置定时任务的信息,例如配置定时任务的名字,群组之类的
        JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)
                .withIdentity(ID + " 01")// 设置Job的标识符
                .storeDurably()// 使JobDetail持久化
                .build();
        //触发器类型
        CronScheduleBuilder scheduleBuilder =
                CronScheduleBuilder.cronSchedule("0/5 * * * * ? *");
        // 创建任务触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(jobDetail) //指定 定时任务
                .withIdentity(ID + " 01Trigger") // 设置Trigger的标识符
                .withSchedule(scheduleBuilder) //配置触发器类型
                .startNow() //立即執行一次任務
                .build();
        // 手动将触发器与任务绑定到调度器内
        scheduler.scheduleJob(jobDetail, trigger);
    }
}

方式二:使用bena的自动配置(建议这种)

这个例子使用的是触发器类型为Simple

package com.example.springbootfull.quartztest.config;

import com.example.springbootfull.quartztest.SecondJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {

    private static final String ID = "AUTOMATIC";

    //配置定时任务的信息,例如配置定时任务的名字,群组之类的
    @Bean
    public JobDetail jobDetail1() {
        return JobBuilder.newJob(SecondJob.class)
                .withIdentity(ID + " 01")// 设置Job的标识符
                .storeDurably()// 使JobDetail持久化
                .build();
    }

    //创建任务触发器
    @Bean
    public SimpleTrigger trigger1() {
        // 简单的调度计划的构造器
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(6) // 频率
                .repeatForever(); // 无限期地重复执行

        return TriggerBuilder.newTrigger()
                .forJob(jobDetail1())//指定 定时任务
                .withIdentity(ID + " 01Trigger")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

运行启动类以后,可以看到如下情况
在这里插入图片描述

3. Quartz的yml配置(按需配置)

yml配置可以更为精细化的,调整 存储配置 及 线程池配置

spring:
  # Quartz 的配置,对应 QuartzProperties 配置类
  quartz:
    job-store-type: memory # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
    auto-startup: true # Quartz 是否自动启动
    startup-delay: 0 # 延迟 N 秒启动
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置
    properties: # 添加 Quartz Scheduler 附加属性
      org:
        quartz:
          threadPool:
            threadCount: 25 # 线程池大小。默认为 10 。
            threadPriority: 5 # 线程优先级
            class: org.quartz.simpl.SimpleThreadPool # 线程池类型
#    jdbc: # 这里暂时不说明,使用 JDBC 的 JobStore 的时候,才需要配置

方式二:数据库(JDBC)方式存储实现定时任务

1. 创建相关表

首先确定maven拉取了 spring-boot-starter-quartz 的依赖,再接着到私仓下面找到
"你的私仓地址\org\quartz-scheduler\quartz\2.3.2"把这个下面的quartz-2.3.2.jar 给解压
然后到这个文件的 “quartz-2.3.2\org\quartz\impl\jdbcjobstore” 下面就可以可以看到

在这里插入图片描述
我这边选择的是“tables_mysql_innodb.sql”脚本
内容如下,执行以后会出现11张表

#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

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;

在这里插入图片描述

表的说明

表名称说明
qrtz_blob_triggersblog类型存储triggers
qrtz_calendars以blog类型存储Calendar信息
qrtz_cron_triggers存储cron trigger信息
qrtz_fired_triggers存储已触发的trigger相关信息
qrtz_job_details存储每一个已配置的job details
qrtz_locks存储悲观锁的信息
qrtz_paused_trigger_grps存储已暂停的trigger组信息
qrtz_scheduler_state存储Scheduler状态信息
qrtz_simple_triggers存储simple trigger信息
qrtz_simprop_triggers存储其他几种trigger信息
qrtz_triggers存储已配置的trigger信息

所有的表中都含有一个SCHED_NAME字段,对应我们配置的scheduler-name,相同 Scheduler-name的节点,形成一个 Quartz 集群。

2. 引入mysql相关依赖

<!-- MySQL连接 -->
<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-plus 这个版本需要指定了,因为场景启动器里面没有 -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.7</version>
</dependency>

3. Quartz的yml配置

这里有个需要注意的
当你的spring版本为spring 2.6.x 版本的 集成Quartz时,如果启动时报错如下

Failed to obtain DB connection from data source ‘quartzDataSource’: java.sql.SQLException: There is no DataSource named ‘quartzDataSource’

解决方法一:去掉配置的org.quartz.jobStore.class属性即可解决该问题。
解决方法二:把 org.quartz.impl.jdbcjobstore.JobStoreTX 改成 org.springframework.scheduling.quartz.LocalDataSourceJobStore

spring:
  datasource:
    quartz:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/quartz?serverTimezone=GMT%2B8
      username: root
      password: root
  quartz:
    job-store-type: jdbc # 使用数据库存储
    scheduler-name: hyhScheduler # 相同 Scheduler 名字的节点,形成一个 Quartz 集群
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    jdbc:
      initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,表示我们手动创建表结构。
    properties:
      org:
        quartz:
          # JobStore 相关配置
          jobStore:
            dataSource: quartzDataSource # 使用的数据源
            #class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_ # Quartz 表前缀
            isClustered: true # 是集群模式
            clusterCheckinInterval: 1000
            useProperties: false
          # 线程池相关配置
          threadPool:
            threadCount: 25 # 线程池大小。默认为 10 。
            threadPriority: 5 # 线程优先级
            class: org.quartz.simpl.SimpleThreadPool # 线程池类型

4. 配置数据源

package com.example.springbootfull.quartztest.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfiguration {

    private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {
        // 创建 HikariDataSource 对象
        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        // 设置线程池名
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        return dataSource;
    }

    /**
     * 创建 quartz 数据源的配置对象
     */
    @Primary
    @Bean(name = "quartzDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.quartz")
    // 读取 spring.datasource.quartz 配置到 DataSourceProperties 对象
    public DataSourceProperties quartzDataSourceProperties() {
        return new DataSourceProperties();
    }

    /**
     * 创建 quartz 数据源
     */
    @Bean(name = "quartzDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.quartz.hikari")
    @QuartzDataSource
    public DataSource quartzDataSource() {
        // 获得 DataSourceProperties 对象
        DataSourceProperties properties = this.quartzDataSourceProperties();
        // 创建 HikariDataSource 对象
        return createHikariDataSource(properties);
    }

}


启动查看

启动启动类后,发现数据库表里面有数据了
在这里插入图片描述

参考文章如下:
【1】SpringBoot整合任务调度框架Quartz及持久化配置
【2】玩转 Spring Boot 集成篇(定时任务框架Quartz)
【3】springboot升级2.6.x,Quartz2.3.2找不到数据源
【4】springboot升级到2.7.17后,quartz集群模式配置修改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2194623.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VirtualBox Ubuntu22.04 NOI linux2.0 Terminal无法打开 终端打不开 两步解决法儿

新安装的虚拟机无法打开Terminal&#xff0c;从应用列表中单击Terminal&#xff0c;左上角任务栏会出现Terminal&#xff0c;并且鼠标转圈&#xff0c;但是过一会左上角Terminal消失&#xff0c;就像一切都没有来过。 解决办法&#xff1a; CTRL ALT F3 进入命令行模式&…

【自然语言处理】补充:基于向量空间的分类器

【自然语言处理】补充:基于向量空间的分类器 文章目录 【自然语言处理】补充:基于向量空间的分类器1. 特征选择2. 基于向量空间的分类方法3. Rocchio4. KNN5. 线性分类器1. 特征选择 特征选择 文本分类中,通常要将文本表示在一个高维空间下,每一维对应一个词项许多维上对应…

如何基于vite实现清除特定环境下的console和debugger

一、解决方法 方法一&#xff1a;使用esbuild 直接在vite.config.ts文件中写&#xff0c;无需下载插件 export default defineConfig(({ mode }) > {// 环境变量const env loadEnv(mode, root, "");return {base: env.VITE_PUBLIC_PATH,plugins: [vue(),...],…

中国书法-孙溟㠭浅析碑帖《九成宫醴泉铭》

中国书法孙溟㠭浅析碑帖《九成宫醴泉铭》 《九成宫醴泉铭》是由魏征撰文、欧阳询书丹&#xff0c;唐贞观六年&#xff08;公元632年&#xff09;立碑&#xff0c;篆书体题碑额。内容记载了唐太宗李世民在九成宫避暑山庄发现涌泉的事。 书法法度森严&#xff0c;腴润中见峭劲&…

图文并茂解释水平分表,垂直分表,水平分库,垂直分库

文章目录 1.垂直角度(表结构不一样)垂直分表:垂直分库&#xff1a; 2.水平角度(表结构一样)水平分库&#xff1a;水平分表&#xff1a; 1.垂直角度(表结构不一样) 垂直分表: 将一个表字段拆分成多个表&#xff0c;每个表存储部分字段。好处是避免IO时锁表的次数&#xff0c;分…

数据结构——遍历二叉树

目录 什么是遍历二叉树 根据遍历序列确定二叉树 例题&#xff08;根据先序中序以及后序中序求二叉树&#xff09; 遍历的算法实现 先序遍历 中序遍历 后序遍历 遍历算法的分析 二叉树的层次遍历 二叉树遍历算法的应用 二叉树的建立 复制二叉树 计算二叉树深度 计算二…

java发起POST方法请求第三方接口(编码处理)

文章目录 引言I 案例查询船舶轨迹配置JVM编码参数请求提供方常见问题II 工具类III 知识扩展:程序运行源代码各个阶段对编码的处理Java源码--->字节码Java字节码--->虚拟机--->操作系统操作系统-->显示设备引言 使用场景: 调用第三方平台接口 I 案例 查询船舶…

【MySQL】--数据类型

文章目录 1. 选择数据库1.1 语法 2. 查询当前选中的数据库2.1 语法 3. 常见数据类型分类4. 数据值类型4.1 类型列表4.2 数据类型取值范围 5. 字符串类型5.1 类型列表5.2 关于排序5.3 CHAR和VARCHAR的区别5.4 如何选择CHAR和VARCHAR5.5 VARCHAR与TEXT的区别 6. 日期类型6.1 类型…

基于SSM的仿win10界面的酒店管理系统

基于SSM的仿win10界面的酒店管理系统 运行环境: jdk1.8 eclipse tomcat7 mysql5.7 项目技术: jspssm&#xff08;springspringmvcmybatis&#xff09;mysql 项目功能模块&#xff1a;基础功能、房间类型、楼层信息、附属功能

重学SpringBoot3-集成Redis(六)之消息队列

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;六&#xff09;之消息队列 1. 什么是发布/订阅&#xff08;Pub/Sub&#xff09;&#xff1f;2. 场景应用3. Spring Boot 3 整合 R…

EtherNet/IP 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 EtherNet/IP 转 EtherNet/IP GW系列型号 MS-GW22 概述 简介 MS-GW22 是 EtherNet/IP 和 EtherNet/IP 协议转换网关&#xff0c;…

4.扩散模型的似然函数最大化(1)

1.似然函数最大化 扩散模型的训练目标是负的对数似然的一个变分下界(VLB)。在本节中&#xff0c;我们总结并调查最近关于扩散模型的似然最大化的工作。首先我们介绍似然函数最大化的意义&#xff0c;然后重点讨论3种类型的方法:噪声调度优化、逆向方差学习和精确的对数似然估计…

20年408数据结构

第一题&#xff1a; 解析&#xff1a;这种题可以先画个草图分析一下&#xff0c;一下就看出来了。 这里的m(7,2)对应的是这图里的m(2,7),第一列存1个元素&#xff0c;第二列存2个元素&#xff0c;第三列存3个元素&#xff0c;第四列存4个元素&#xff0c;第五列存5个元素&#…

胤娲科技:00后揭秘——AI大模型的可靠性迷局

当智能不再“靠谱”&#xff0c;我们该何去何从&#xff1f; 想象一下&#xff0c;你向最新的GPT模型提问&#xff1a;“9.9和9.11哪个大&#xff1f;”这本应是个小菜一碟的问题&#xff0c;却足以让不少高科技的“大脑”陷入沉思&#xff0c; 甚至给出令人啼笑皆非的答案。近…

卡码网104.建造最大岛屿

题目 104. 建造最大岛屿 (kamacoder.com) 代码&#xff08;ACM 首刷看解析&#xff09;&#xff1a; #include<iostream> #include<vector> #include<unordered_map> #include<unordered_set> using namespace std;int dir[4][2] {1,0,-1,0,0,1,0,-…

C++ 算法学习——1.8 悬线法

1.问题引入&#xff1a;对于一个矩形图&#xff0c;图中放置着不少障碍&#xff0c;要求出最大的不含障碍的矩形。 2.分析&#xff1a;显然一个极大矩形是左右上下都被障碍挡住&#xff0c;无法再扩大的矩形&#xff0c;此时障碍也包括边界。 3.方法&#xff1a;悬线法考虑以…

计算机组成原理实验三 数据寄存器组R0..R3, MAR, ST, OUT

实验目的和要求 目的&#xff1a;了解模型机中各种寄存器结构、工作原理及其控制方法。 要求&#xff1a;利用CP226 实验系统上的K16..K23 开关做为DBUS 的数据&#xff0c;其它开关做为控制信号&#xff0c;将数据写入寄存器&#xff0c;数据寄存器组R0..R3&#xff0c;地址…

【大数据】Flink CDC 实时同步mysql数据

目录 一、前言 二、Flink CDC介绍 2.1 什么是Flink CDC 2.2 Flink CDC 特点 2.3 Flink CDC 核心工作原理 2.4 Flink CDC 使用场景 三、常用的数据同步方案对比 3.1 数据同步概述 3.1.1 数据同步来源 3.2 常用的数据同步方案汇总 3.3 为什么推荐Flink CDC 3.4 Flink …

进程间通信(匿名管道 创建管道及分配任务代码)

文章目录 一.进程间通信进程为什么要通信&#xff1f;进程如何通信 二.管道匿名管道pipe写端慢写入&#xff0c;读端等待写端写入&#xff0c;读端不读 && 管道的大小写端关闭&#xff0c;读端不会读取写端写入&#xff0c;读端关闭字节流 总结安全问题 三.进程池创建管…

VADv2 论文学习

VADv2: End-to-End Vectorized Autonomous Driving via Probabilistic Planning 解决了什么问题&#xff1f;相关工作感知运动预测规划自动驾驶领域的大语言模型 提出了什么方法&#xff1f;场景编码器概率规划训练分布损失冲突损失场景 Token 损失 推理 实验实验设定指标消融实…