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

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

  • 简介
  • 适用场景
  • Quartz核心概念
  • Quartz 存储方式
  • Quartz 版本类型
  • 引入相关依赖
  • 开始集成
    • 方式一:内存方式(MEMORY)存储实现定时任务
      • 1. 定义任务类
      • 2. 定义任务描述及创建任务触发器
      • 3. Quartz的yml配置(按需配置)
    • 方式二:数据库(JDBC)方式存储实现定时任务
      • 1. 创建相关表
      • 2. 引入mysql相关依赖
      • 3. 添加yml配置及相关配置类
      • 启动查看
    • 防止并发执行:上一周期还没执行完,下一周期又开始了
  • 遇到的问题及解决
    • 问题1:更改 Quartz 的 默认连接池配置
    • 问题2:找不到名为 quartzDataSource 的数据源
    • 问题3:jdbcUrl is required with driverClassName
    • 问题4:url is required with driverClassName

简介

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 {@Overrideprotected 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 {@Overrideprotected 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";@Autowiredprivate Scheduler scheduler;@Overridepublic 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";//配置定时任务的信息,例如配置定时任务的名字,群组之类的@Beanpublic JobDetail jobDetail1() {return JobBuilder.newJob(SecondJob.class).withIdentity(ID + " 01")// 设置Job的标识符.storeDurably()// 使JobDetail持久化.build();}//创建任务触发器@Beanpublic 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 ,建议设置为 trueoverwrite-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. 添加yml配置及相关配置类

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

新建DataSourceConfiguration 配置类

该类为数据源配置类
目前只是配置了quartz 数据源,暂不支持多数据源
如需多数据源配置,请往下看

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;/*** 该类为数据源配置类* 目前只是配置了quartz 数据源,暂不支持多数据源* 如需多数据源配置,请自行补充*/
@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")@QuartzDataSourcepublic DataSource quartzDataSource() {// 获得 DataSourceProperties 对象DataSourceProperties properties = this.quartzDataSourceProperties();// 创建 HikariDataSource 对象return createHikariDataSource(properties);}}

启动查看

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

防止并发执行:上一周期还没执行完,下一周期又开始了

使用 @DisallowConcurrentExecution 注解:这可以确保同一时间只有一个该任务的实例在执行,从而避免并发执行带来的问题。
但是,这可能会导致任务执行的延迟,因为新实例必须等待前一个实例完成。

举例:

@Slf4j
@DisallowConcurrentExecution
public class SecondJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {JobDetail jobDetail = jobExecutionContext.getJobDetail();log.info("------任务名:" + jobDetail.getKey().getName() + ",组名:" +jobDetail.getKey().getGroup() + "------我是要执行的定时任务工作内容!");}
}

遇到的问题及解决

问题1:更改 Quartz 的 默认连接池配置

Quartz 2.0 以前 需要使用的 DBCP 作为数据库连接池
Quartz 2.0 以后 C3P0(包含2.0)需要使用 C3P0 作为数据库连接池

这个时候我们就需要更改这个连接池,改用 HikariCP 或者 Druid

方式一:关键属性 provider :

#指定的数据源名称
spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobs
#指定数据库连接池
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.provider=hikaricp

方式二:初始化为 HikariDataSource 的数据源

 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;}

问题2:找不到名为 quartzDataSource 的数据源

当你的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

问题3:jdbcUrl is required with driverClassName

使用了HikariCP连接池时,spring.datasource.jdbc-url 才是有效属性

问题4:url is required with driverClassName

没有使用HikariCP连接池时,spring.datasource.url 才是有效属性

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Broken pipe异常分析及处理

问题出现&#xff1a;生产上运行的系统业务正常&#xff0c;当在查询数据时&#xff0c;出现后台异常&#xff0c;检查后台日志出现Broken Pipe异常&#xff1b; 如图示&#xff1a; Broken Pipe定义&#xff1a;通常发生在服务器端尝试向已关闭的套接字&#xff08;客户端/端…

前端面试经验总结1(简历篇)

本文分为3部分&#xff0c;分别为第一部分简历篇&#xff0c;第二部分经典问题篇以及第三部分知识体系篇&#xff0c;都是个人面试经验及同行面试经验总结和整理。 我对于简历的理解是这样的&#xff0c;简历的作用是让看简历的人能够快速、准确地捕捉到有用信息&#xff1a; 你…

大数据存储,搜索智能化的实践分享 | OceanBase 城市交流会精彩回顾

9月21日&#xff0c;“OceanBase 城市交流会”来到了深圳&#xff0c;携手货拉拉大数据技术与产品部&#xff0c;联合举办了“走进货拉拉”的技术交流活动。货拉拉、万家数科、云集、百丽等多家企业的一线技术专家&#xff0c;就大数据存储、AI等热点话题&#xff0c;深入探讨并…

新的一轮前端面试已来临,避坑指南来 get!

最近看到很多人都在投简历&#xff0c;可是很多人对待面试不够认真&#xff0c;只会等待结果&#xff0c;不去努力。所以整理一些懒人面试技巧给大家。 我们说说目前应用面积最广的 Vue&#xff0c;大厂或是高级工程师面试的时候究竟会碰到什么棘手或是难缠的 Vue 的面试题呢&a…

人工智能 | openai chatgpt 大语言模型

简介 OpenAI 一个美国人工智能研究实验室&#xff0c;由非营利组织 OpenAI Inc&#xff0c;和其营利组织子公司 OpenAI LP 所组成。该组织于 2015 年由萨姆阿尔特曼、里德霍夫曼、杰西卡利文斯顿、伊隆马斯克、伊尔亚苏茨克维、沃伊切赫萨伦巴、彼得泰尔等人在旧金山成立&…

高效数据处理:MapReduce与Hive的实战应用

文章目录 hive分析汇总互联网日志分析1.项目需求2.数据说明3.算法思路 用户电影推荐1.项目需求2.数据说明3.算法思路4.解题步骤 简单数据统计WordCount数据说明 疫情数据分析1.项目需求2.数据说明step1:创建ods层数据表step2&#xff1a;创建dwd层数据表step3&#xff1a;创建d…

selenium的webdriver常用方法和属性介绍(2)

selenium的webdriver介绍 从selenium导入webdriver模块&#xff0c;在pycharm中跳转webdriver模块的__init__.py文件&#xff0c;内容如图所示&#xff1a;从selenium包的子目录中导入了很多模块并做了重命名&#xff0c;用于支持如下 Chrome/Edge/Ie/Firefox/Safari浏览器。 使…

Codigger Keyboard Look应用说明

Keyboard Look是一项专为Desktop设计的功能模式&#xff0c;旨在通过全面启用键盘操作&#xff0c;减少对鼠标依赖&#xff0c;从而提升用户的工作效率和操作体验。尤其对于频繁使用键盘的用户&#xff0c;如开发者&#xff0c;此模式可显著加速日常操作&#xff0c;优化工作流…

连肝了多天学习MySQL索引与性能优化,详细总结一下索引的使用与数据库优化

文章目录 索引是什么&#xff1f;索引的作用初步认识索引索引的类型按照数据结构分类BTREE索引 哈希索引 按功能逻辑进行分类唯一索引普通索引主键索引全文索引 按照字段的个数进行划分单列索引多列&#xff08;组合&#xff0c;联合&#xff09;索引 小结索引的设计原则数据准…

Centos7 搭建单机elasticsearch

以下是在 CentOS 7 上安装 Elasticsearch 7.17.7 的完整步骤&#xff1a;&#xff08;数据默认保存在/var/lib/elasticsearch下&#xff0c;自行更改&#xff09; 一、装 Java 环境 Elasticsearch 是用 Java 编写的&#xff0c;所以需要先安装 Java 运行环境。 检查系统中是…

gbase8s之建表相关问题

第一章..绪论 1.1..背景 需要对明年所有系统的表新建。 1.2..要求 对导切建表可能遇到的一些问题罗列及解决办法。 第二章..新建表的的过程 1.1..获取DDL 获取DDL一定要在服务器上去获取&#xff0c;千万别用gds去导出ddl。 1.1.1..切换数据库用户 su – gbasedbt 1.1…

一键录制电脑画面全攻略!免费的电脑录屏秘籍

屏幕录制&#xff0c;这是个看起来挺简单&#xff0c;实际上作用可大了的工具。它能帮咱们把电脑屏幕上的每个动作都给抓住&#xff0c;不管是弄教程&#xff0c;还是记会议&#xff0c;又或者是分享游戏里的精彩时刻。不过&#xff0c;一键录制真有它听上去那么容易吗&#xf…

1.Label Studio 介绍

Label Studio 介绍 文章目录 Label Studio 介绍前言一、安装介绍二、Run with Docker Compose1、WSL2安装2、Docker Desktop安装3、Label Studio安装&#xff08;第二种方法 Run with Docker Compose &#xff09; 三、Install for local development1.下载源码2.安装poetry3.安…

了解计算机视觉:探索直播平台视频美颜SDK的集成与性能调优

本篇文章。小编将详细探讨计算机视觉在直播中的应用&#xff0c;尤其是如何集成视频美颜SDK并进行性能调优。 一、计算机视觉与视频美颜的基本原理 计算机视觉是人工智能的一个重要分支&#xff0c;旨在让机器能够“看见”并理解图像或视频中的内容。在直播场景中&#xff0c…

ECCV`24 | 新加坡国立华为提出Vista3D: 实现快速且多视角一致的3D生成

文章链接&#xff1a;https://arxiv.org/pdf/2409.12193 gitbub链接&#xff1a;https://github.com/florinshen/Vista3D 亮点直击 提出了Vista3D&#xff0c;一个用于揭示单张图像3D darkside 的框架&#xff0c;能够高效地利用2D先验生成多样的3D物体。开发了一种从高斯投影到…

红外传感器的算法!

一、红外线的基本性质 红外线&#xff0c;又称红外光&#xff0c;是电磁波谱中波长介于可见光和微波之间的部分。它具有反射、折射、散射、干涉和吸收等物理特性&#xff0c;这些特性使得红外线传感器能够实现对物体的非接触式检测。 二、红外传感器的算法原理 热电效应&…

计算机网络:物理层 —— 数据的传输方式

文章目录 传输方式串行传输串行传输方式特点应用 并行传输特点应用 网卡的串/并转换同步传输同步时钟频率的误差问题特点应用 异步传输特点应用 单向通信特点应用 双向交替通信特点应用 双向同时通信特点应用 传输方式 串行传输 串行传输是一种数据传输方式&#xff0c;指的是…

双十一买什么东西比较好?2024双11好物攻略诚意分享

双十一活动已经准备要开始了&#xff0c;很多人摩拳擦掌准备入手划算好物&#xff0c;但是有的人也比较迷茫&#xff0c;双十一买什么东西比较好&#xff1f;为了帮助大家在这场购物狂欢中理性消费&#xff0c;提升购物体验&#xff0c;我们精心整理了一份2024双11好物攻略诚意…

【树莓派5B】移植yolo5-lite

移植yolo5-lite 前言一、获取Lite文件二、创建环境2.1 创建虚拟环境2.2 激活虚拟环境2.2 配置环境 三、运行detect.py程序总结 前言 参考文献 获取文件看他研究僧树莓派5的学习记录13——yolov5-lite部署树莓派 树莓派5B快速YOLOV5环境搭建 一、获取Lite文件 根据up&#x1f…

解决:Ubuntu连接不上网络

今天莫名奇妙&#xff0c;我的ubuntu20.04断网了。检查了一下&#xff0c;使用的也是桥接模式&#xff0c;啥也没有变化。 然后我上Ubuntu16.04版本看了&#xff0c;那里又可以成功上网&#xff0c;所以&#xff0c;不是电脑的问题。 看了网上两个教程&#xff0c;解决了。 …