WordPress前端上傳大文件搜索引擎優(yōu)化seo名詞解釋
SpringBoot教程(二十四) | SpringBoot實(shí)現(xiàn)分布式定時(shí)任務(wù)之Quartz(基礎(chǔ))
- 簡(jiǎn)介
- 適用場(chǎng)景
- Quartz核心概念
- Quartz 存儲(chǔ)方式
- Quartz 版本類型
- 引入相關(guān)依賴
- 開(kāi)始集成
- 方式一:內(nèi)存方式(MEMORY)存儲(chǔ)實(shí)現(xiàn)定時(shí)任務(wù)
- 1. 定義任務(wù)類
- 2. 定義任務(wù)描述及創(chuàng)建任務(wù)觸發(fā)器
- 3. Quartz的yml配置(按需配置)
- 方式二:數(shù)據(jù)庫(kù)(JDBC)方式存儲(chǔ)實(shí)現(xiàn)定時(shí)任務(wù)
- 1. 創(chuàng)建相關(guān)表
- 2. 引入mysql相關(guān)依賴
- 3. 添加yml配置及相關(guān)配置類
- 啟動(dòng)查看
- 防止并發(fā)執(zhí)行:上一周期還沒(méi)執(zhí)行完,下一周期又開(kāi)始了
- 遇到的問(wèn)題及解決
- 問(wèn)題1:更改 Quartz 的 默認(rèn)連接池配置
- 問(wèn)題2:找不到名為 quartzDataSource 的數(shù)據(jù)源
- 問(wèn)題3:jdbcUrl is required with driverClassName
- 問(wèn)題4:url is required with driverClassName
簡(jiǎn)介
Quartz是一個(gè)完全由Java開(kāi)發(fā)的開(kāi)源任務(wù)日程管理系統(tǒng)(或稱為作業(yè)調(diào)度框架),它能夠集成于任何Java應(yīng)用,小到獨(dú)立的應(yīng)用,大至電子商務(wù)系統(tǒng)。
Quartz提供了豐富的調(diào)度功能和靈活的配置選項(xiàng),幫助開(kāi)發(fā)者實(shí)現(xiàn)復(fù)雜的任務(wù)調(diào)度和定時(shí)任務(wù)功能。
適用場(chǎng)景
Quartz廣泛應(yīng)用于各種企業(yè)級(jí)應(yīng)用系統(tǒng)中,如電子商務(wù)、金融、物流等領(lǐng)域。
無(wú)論是簡(jiǎn)單的定時(shí)任務(wù)(如定時(shí)發(fā)送郵件、定時(shí)清理臨時(shí)文件等),還是復(fù)雜的分布式任務(wù)(如分布式定時(shí)任務(wù)調(diào)度、任務(wù)依賴關(guān)系管理等),Quartz都能夠勝任。
Quartz核心概念
Job(作業(yè))
Quartz中的任務(wù),是一個(gè)需要被調(diào)度執(zhí)行的接口,任務(wù)類需要實(shí)現(xiàn)該接口,并在execute方法中編寫具體的業(yè)務(wù)邏輯。
JobDetail
JobDetail用來(lái)綁定Job,并為Job實(shí)例提供許多屬性,如名稱、組名、Job類名以及JobDataMap等。
JobDetail定義的是任務(wù)數(shù)據(jù),而真正的執(zhí)行邏輯是在Job中。
Trigger(觸發(fā)器)
Trigger是Quartz的觸發(fā)器,它描述了觸發(fā)Job執(zhí)行的時(shí)間觸發(fā)規(guī)則。
Trigger最常用的觸發(fā)器類型:SimpleTrigger和CronTrigger。
Scheduler(調(diào)度器)
Scheduler是Quartz的調(diào)度器,它負(fù)責(zé)基于Trigger設(shè)定的時(shí)間執(zhí)行Job。Scheduler是一個(gè)容器,它裝載著任務(wù)和觸發(fā)器。
Quartz 存儲(chǔ)方式
Quartz 存儲(chǔ)方式有兩種:MEMORY 和 JDBC。
- MEMORY(或RAMJobStore):這是Quartz的默認(rèn)存儲(chǔ)方式,它將任務(wù)調(diào)度的運(yùn)行信息保存在內(nèi)存中。這種方式提供了最佳的性能,因?yàn)閮?nèi)存中數(shù)據(jù)訪問(wèn)最快。然而,它的不足之處在于缺乏數(shù)據(jù)的持久性,當(dāng)程序中途停止或系統(tǒng)崩潰時(shí),所有運(yùn)行的信息都會(huì)丟失。
- JDBC:這種存儲(chǔ)方式允許Quartz通過(guò)JDBC將任務(wù)調(diào)度的運(yùn)行信息保存到數(shù)據(jù)庫(kù)中。使用數(shù)據(jù)庫(kù)保存任務(wù)調(diào)度信息后,即使系統(tǒng)崩潰后重新啟動(dòng),任務(wù)的調(diào)度信息也將得到恢復(fù)。因此,JDBC存儲(chǔ)方式提供了數(shù)據(jù)的持久性。
Quartz 版本類型
Quartz 版本類型有兩種:單機(jī)版 和 集群版
-
單機(jī)版:這是Quartz的默認(rèn)版本類型 ,Quartz運(yùn)行在一個(gè)單一的Java虛擬機(jī)(JVM)實(shí)例中。它通常使用MEMORY存儲(chǔ)方式,因?yàn)檫@種方式配置容易且運(yùn)行速度快。然而,單機(jī)版Quartz存在單點(diǎn)故障的風(fēng)險(xiǎn),如果應(yīng)用程序所在的服務(wù)器出現(xiàn)故障,任務(wù)調(diào)度將會(huì)停止。
-
集群版:集群版Quartz可以在多個(gè)JVM實(shí)例中運(yùn)行,實(shí)現(xiàn)高可用性和負(fù)載均衡。在集群版中,通常使用JDBC存儲(chǔ)方式,以便在多個(gè)節(jié)點(diǎn)之間共享任務(wù)調(diào)度的數(shù)據(jù)。這樣,即使其中一個(gè)節(jié)點(diǎn)出現(xiàn)故障,其他節(jié)點(diǎn)仍然可以繼續(xù)工作,從而保證了任務(wù)調(diào)度的連續(xù)性和可靠性。
引入相關(guān)依賴
<!--quartz定時(shí)任務(wù)-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
開(kāi)始集成
方式一:內(nèi)存方式(MEMORY)存儲(chǔ)實(shí)現(xiàn)定時(shí)任務(wù)
1. 定義任務(wù)類
可以通過(guò)實(shí)現(xiàn) Job 接口來(lái)定義任務(wù),也可以通過(guò)繼承 QuartzJobBean 這個(gè)抽象類來(lái)定義任務(wù),其實(shí) QuartzJobBean 本身也實(shí)現(xiàn)了 Job 接口,其本質(zhì)都是實(shí)現(xiàn) Job 接口來(lái)定義任務(wù)。
我這邊定義了兩個(gè)任務(wù),用于后續(xù)來(lái)說(shuō)明一下關(guān)于調(diào)度器Scheduler綁定的不同方式
Scheduler綁定有兩種方式,一種是使用bena的自動(dòng)配置,一種是Scheduler手動(dòng)配置。
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("手動(dòng)-FirstJob, 當(dāng)前的時(shí)間: " + 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("自動(dòng)-SecondJob, 當(dāng)前的時(shí)間: " + now);}
}
2. 定義任務(wù)描述及創(chuàng)建任務(wù)觸發(fā)器
方式一:Scheduler手動(dòng)配置
這個(gè)例子使用的是觸發(fā)器類型為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 {// 配置定時(shí)任務(wù)的信息,例如配置定時(shí)任務(wù)的名字,群組之類的JobDetail jobDetail = JobBuilder.newJob(FirstJob.class).withIdentity(ID + " 01")// 設(shè)置Job的標(biāo)識(shí)符.storeDurably()// 使JobDetail持久化.build();//觸發(fā)器類型CronScheduleBuilder scheduleBuilder =CronScheduleBuilder.cronSchedule("0/5 * * * * ? *");// 創(chuàng)建任務(wù)觸發(fā)器Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail) //指定 定時(shí)任務(wù).withIdentity(ID + " 01Trigger") // 設(shè)置Trigger的標(biāo)識(shí)符.withSchedule(scheduleBuilder) //配置觸發(fā)器類型.startNow() //立即執(zhí)行一次任務(wù).build();// 手動(dòng)將觸發(fā)器與任務(wù)綁定到調(diào)度器內(nèi)scheduler.scheduleJob(jobDetail, trigger);}
}
方式二:使用bena的自動(dòng)配置(建議這種)
這個(gè)例子使用的是觸發(fā)器類型為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";//配置定時(shí)任務(wù)的信息,例如配置定時(shí)任務(wù)的名字,群組之類的@Beanpublic JobDetail jobDetail1() {return JobBuilder.newJob(SecondJob.class).withIdentity(ID + " 01")// 設(shè)置Job的標(biāo)識(shí)符.storeDurably()// 使JobDetail持久化.build();}//創(chuàng)建任務(wù)觸發(fā)器@Beanpublic SimpleTrigger trigger1() {// 簡(jiǎn)單的調(diào)度計(jì)劃的構(gòu)造器SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(6) // 頻率.repeatForever(); // 無(wú)限期地重復(fù)執(zhí)行return TriggerBuilder.newTrigger().forJob(jobDetail1())//指定 定時(shí)任務(wù).withIdentity(ID + " 01Trigger").withSchedule(scheduleBuilder).build();}
}
運(yùn)行啟動(dòng)類以后,可以看到如下情況
3. Quartz的yml配置(按需配置)
yml配置可以更為精細(xì)化的,調(diào)整 存儲(chǔ)配置 及 線程池配置
spring:# Quartz 的配置,對(duì)應(yīng) QuartzProperties 配置類quartz:job-store-type: memory # Job 存儲(chǔ)器類型。默認(rèn)為 memory 表示內(nèi)存,可選 jdbc 使用數(shù)據(jù)庫(kù)。auto-startup: true # Quartz 是否自動(dòng)啟動(dòng)startup-delay: 0 # 延遲 N 秒啟動(dòng)wait-for-jobs-to-complete-on-shutdown: true # 應(yīng)用關(guān)閉時(shí),是否等待定時(shí)任務(wù)執(zhí)行完成。默認(rèn)為 false ,建議設(shè)置為 trueoverwrite-existing-jobs: false # 是否覆蓋已有 Job 的配置properties: # 添加 Quartz Scheduler 附加屬性org:quartz:threadPool:threadCount: 25 # 線程池大小。默認(rèn)為 10 。threadPriority: 5 # 線程優(yōu)先級(jí)class: org.quartz.simpl.SimpleThreadPool # 線程池類型
# jdbc: # 這里暫時(shí)不說(shuō)明,使用 JDBC 的 JobStore 的時(shí)候,才需要配置
方式二:數(shù)據(jù)庫(kù)(JDBC)方式存儲(chǔ)實(shí)現(xiàn)定時(shí)任務(wù)
1. 創(chuàng)建相關(guān)表
首先確定maven拉取了 spring-boot-starter-quartz 的依賴,再接著到私倉(cāng)下面找到
"你的私倉(cāng)地址\org\quartz-scheduler\quartz\2.3.2"把這個(gè)下面的quartz-2.3.2.jar 給解壓
然后到這個(gè)文件的 “quartz-2.3.2\org\quartz\impl\jdbcjobstore” 下面就可以可以看到
我這邊選擇的是“tables_mysql_innodb.sql”腳本
內(nèi)容如下,執(zhí)行以后會(huì)出現(xiàn)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;
表的說(shuō)明
表名稱 | 說(shuō)明 |
---|---|
qrtz_blob_triggers | blog類型存儲(chǔ)triggers |
qrtz_calendars | 以blog類型存儲(chǔ)Calendar信息 |
qrtz_cron_triggers | 存儲(chǔ)cron trigger信息 |
qrtz_fired_triggers | 存儲(chǔ)已觸發(fā)的trigger相關(guān)信息 |
qrtz_job_details | 存儲(chǔ)每一個(gè)已配置的job details |
qrtz_locks | 存儲(chǔ)悲觀鎖的信息 |
qrtz_paused_trigger_grps | 存儲(chǔ)已暫停的trigger組信息 |
qrtz_scheduler_state | 存儲(chǔ)Scheduler狀態(tài)信息 |
qrtz_simple_triggers | 存儲(chǔ)simple trigger信息 |
qrtz_simprop_triggers | 存儲(chǔ)其他幾種trigger信息 |
qrtz_triggers | 存儲(chǔ)已配置的trigger信息 |
所有的表中都含有一個(gè)SCHED_NAME字段,對(duì)應(yīng)我們配置的scheduler-name,相同 Scheduler-name的節(jié)點(diǎn),形成一個(gè) Quartz 集群。
2. 引入mysql相關(guān)依賴
<!-- MySQL連接 -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-plus 這個(gè)版本需要指定了,因?yàn)閳?chǎng)景啟動(dòng)器里面沒(méi)有 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.7</version>
</dependency>
3. 添加yml配置及相關(guān)配置類
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 # 使用數(shù)據(jù)庫(kù)存儲(chǔ)scheduler-name: hyhScheduler # 相同 Scheduler 名字的節(jié)點(diǎn),形成一個(gè) Quartz 集群wait-for-jobs-to-complete-on-shutdown: true # 應(yīng)用關(guān)閉時(shí),是否等待定時(shí)任務(wù)執(zhí)行完成。默認(rèn)為 false ,建議設(shè)置為 truejdbc:initialize-schema: never # 是否自動(dòng)使用 SQL 初始化 Quartz 表結(jié)構(gòu)。這里設(shè)置成 never ,表示我們手動(dòng)創(chuàng)建表結(jié)構(gòu)。properties:org:quartz:# JobStore 相關(guān)配置jobStore:# 使用的數(shù)據(jù)源 (和配置類DataSourceConfiguration的 @Bean(name = "quartzDataSource") 存在直接關(guān)系)dataSource: quartzDataSource #class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 實(shí)現(xiàn)類driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegatetablePrefix: QRTZ_ # Quartz 表前綴isClustered: true # 是集群模式clusterCheckinInterval: 1000useProperties: false# 線程池相關(guān)配置threadPool:threadCount: 25 # 線程池大小。默認(rèn)為 10 。threadPriority: 5 # 線程優(yōu)先級(jí)class: org.quartz.simpl.SimpleThreadPool # 線程池類型
新建DataSourceConfiguration 配置類
該類為數(shù)據(jù)源配置類
目前只是配置了quartz 數(shù)據(jù)源,暫不支持多數(shù)據(jù)源
如需多數(shù)據(jù)源配置,請(qǐng)往下看
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;/*** 該類為數(shù)據(jù)源配置類* 目前只是配置了quartz 數(shù)據(jù)源,暫不支持多數(shù)據(jù)源* 如需多數(shù)據(jù)源配置,請(qǐng)自行補(bǔ)充*/
@Configuration
public class DataSourceConfiguration {private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {// 創(chuàng)建 HikariDataSource 對(duì)象HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();// 設(shè)置線程池名if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}/*** 創(chuàng)建 quartz 數(shù)據(jù)源的配置對(duì)象*/@Primary@Bean(name = "quartzDataSourceProperties")@ConfigurationProperties(prefix = "spring.datasource.quartz")// 讀取 spring.datasource.quartz 配置到 DataSourceProperties 對(duì)象public DataSourceProperties quartzDataSourceProperties() {return new DataSourceProperties();}/*** 創(chuàng)建 quartz 數(shù)據(jù)源*/@Bean(name = "quartzDataSource")//@ConfigurationProperties(prefix = "spring.datasource.quartz.hikari")@QuartzDataSourcepublic DataSource quartzDataSource() {// 獲得 DataSourceProperties 對(duì)象DataSourceProperties properties = this.quartzDataSourceProperties();// 創(chuàng)建 HikariDataSource 對(duì)象return createHikariDataSource(properties);}}
啟動(dòng)查看
啟動(dòng)啟動(dòng)類后,發(fā)現(xiàn)數(shù)據(jù)庫(kù)表里面有數(shù)據(jù)了
防止并發(fā)執(zhí)行:上一周期還沒(méi)執(zhí)行完,下一周期又開(kāi)始了
使用 @DisallowConcurrentExecution 注解:這可以確保同一時(shí)間只有一個(gè)該任務(wù)的實(shí)例在執(zhí)行,從而避免并發(fā)執(zhí)行帶來(lái)的問(wèn)題。
但是,這可能會(huì)導(dǎo)致任務(wù)執(zhí)行的延遲,因?yàn)樾聦?shí)例必須等待前一個(gè)實(shí)例完成。
舉例:
@Slf4j
@DisallowConcurrentExecution
public class SecondJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {JobDetail jobDetail = jobExecutionContext.getJobDetail();log.info("------任務(wù)名:" + jobDetail.getKey().getName() + ",組名:" +jobDetail.getKey().getGroup() + "------我是要執(zhí)行的定時(shí)任務(wù)工作內(nèi)容!");}
}
遇到的問(wèn)題及解決
問(wèn)題1:更改 Quartz 的 默認(rèn)連接池配置
Quartz 2.0 以前 需要使用的 DBCP 作為數(shù)據(jù)庫(kù)連接池
Quartz 2.0 以后 C3P0(包含2.0)需要使用 C3P0 作為數(shù)據(jù)庫(kù)連接池
這個(gè)時(shí)候我們就需要更改這個(gè)連接池,改用 HikariCP 或者 Druid
方式一:關(guān)鍵屬性 provider :
#指定的數(shù)據(jù)源名稱
spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobs
#指定數(shù)據(jù)庫(kù)連接池
spring.quartz.properties.org.quartz.dataSource.quartz_jobs.provider=hikaricp
方式二:初始化為 HikariDataSource 的數(shù)據(jù)源
private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {// 創(chuàng)建 HikariDataSource 對(duì)象HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();// 設(shè)置線程池名if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}
問(wèn)題2:找不到名為 quartzDataSource 的數(shù)據(jù)源
當(dāng)你的spring版本為spring 2.6.x 版本的 集成Quartz時(shí),如果啟動(dòng)時(shí)報(bào)錯(cuò)如下
Failed to obtain DB connection from data source ‘quartzDataSource’: java.sql.SQLException: There is no DataSource named ‘quartzDataSource’
解決方法一:去掉配置的org.quartz.jobStore.class屬性即可解決該問(wèn)題。
解決方法二:把 org.quartz.impl.jdbcjobstore.JobStoreTX 改成 org.springframework.scheduling.quartz.LocalDataSourceJobStore
問(wèn)題3:jdbcUrl is required with driverClassName
使用了HikariCP連接池時(shí),spring.datasource.jdbc-url 才是有效屬性
問(wèn)題4:url is required with driverClassName
沒(méi)有使用HikariCP連接池時(shí),spring.datasource.url 才是有效屬性
參考文章如下:
【1】SpringBoot整合任務(wù)調(diào)度框架Quartz及持久化配置
【2】玩轉(zhuǎn) Spring Boot 集成篇(定時(shí)任務(wù)框架Quartz)
【3】springboot升級(jí)2.6.x,Quartz2.3.2找不到數(shù)據(jù)源
【4】springboot升級(jí)到2.7.17后,quartz集群模式配置修改
【5】Quartz配置Springboot自帶連接池Hikaricp
【6】定時(shí)任務(wù)Quartz總結(jié)