目录
准备知识点什么是QuartzQuartz的体系结构什么是Quartz持久化实现案例 - 单实例方式实现案例 - 分布式方式后端实现前端实现测试效果准备知识点
需要了解常用的Quartz框架。
什么是Quartz
来源百度百科, 官网地址:http://www.quartz-scheduler.org/
(资料图片仅供参考)
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。
它的特点如下
纯java实现,可以作为独立的应用程序,也可以嵌入在另一个独立式应用程序运行强大的调度功能,Spring默认的调度框架,灵活可配置;作业持久化,调度环境持久化机制,可以保存并恢复调度现场。系统关闭数据不会丢失;灵活的应用方式,可以任意定义触发器的调度时间表,支持任务和调度各种组合,组件式监听器、各种插件、线程池等功能,多种存储方式等;分布式和集群能力,可以被实例化,一个Quartz集群中的每个节点作为一个独立的Quartz使用,通过相同的数据库表来感知到另一个Quartz应用Quartz的体系结构
Job表示一个工作,要执行的具体内容。JobDetail表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。Trigger代表一个调度参数的配置,什么时候去调。Scheduler代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。什么是Quartz持久化
为什么要持久化?
当程序突然被中断时,如断电,内存超出时,很有可能造成任务的丢失。 可以将调度信息存储到数据库里面,进行持久化,当程序被中断后,再次启动,仍然会保留中断之前的数据,继续执行,而并不是重新开始。
Quartz提供了两种持久化方式
Quartz提供两种基本作业存储类型:
RAMJobStore
在默认情况下Quartz将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快。不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失。
JobStoreTX
所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务。
实现案例 - 单实例方式
本例将展示quartz实现单实例方式。
引入POM依赖
定义Job
只需要继承QuartzJobBean,并重载executeInternal方法即可定义你自己的Job执行逻辑。
@Slf4j
public class HelloJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// get parameters
context.getJobDetail().getJobDataMap().forEach(
(k, v) -> log.info("param, key:{}, value:{}", k, v)
);
// your logics
log.info("Hello Job执行时间: " + new Date());
}
}
配置Job
JobDetail, Trigger, Schedule(这里采用CronScheduleBuilder)
/**
* @author pdai
*/
@Configuration
public class QuartzConfig {
@Bean("helloJob")
public JobDetail helloJobDetail() {
return JobBuilder.newJob(HelloJob.class)
.withIdentity("DateTimeJob")
.usingJobData("msg", "Hello Quartz")
.storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
.build();
}
@Bean
public Trigger printTimeJobTrigger() {
// 每秒执行一次
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(helloJobDetail())
.withIdentity("quartzTaskService")
.withSchedule(cronScheduleBuilder)
.build();
}
}
执行测试
2021-10-01 13:09:00.380 INFO 38484 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-10-01 13:09:00.391 INFO 38484 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-10-01 13:09:00.392 INFO 38484 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.50] 2021-10-01 13:09:00.526 INFO 38484 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-10-01 13:09:00.526 INFO 38484 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1424 ms 2021-10-01 13:09:00.866 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor 2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl 2021-10-01 13:09:00.877 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Quartz Scheduler v.2.3.2 created. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.3.2) "quartzScheduler" with instanceId "NON_CLUSTERED" Scheduler class: "org.quartz.core.QuartzScheduler" - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool "org.quartz.simpl.SimpleThreadPool" - with 10 threads. Using job-store "org.quartz.simpl.RAMJobStore" - which does not support persistence. and is not clustered. 2021-10-01 13:09:00.878 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler "quartzScheduler" initialized from an externally provided properties instance. 2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.3.2 2021-10-01 13:09:00.879 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@6075b2d3 2021-10-01 13:09:00.922 INFO 38484 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path "" 2021-10-01 13:09:00.923 INFO 38484 --- [ main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now 2021-10-01 13:09:00.923 INFO 38484 --- [ main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started. 2021-10-01 13:09:00.933 INFO 38484 --- [ main] tech.pdai.springboot.quartz.App : Started App in 2.64 seconds (JVM running for 3.621) 2021-10-01 13:09:00.931 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:00.933 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:00 CST 2021 2021-10-01 13:09:01.001 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:01.001 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:01 CST 2021 2021-10-01 13:09:02.000 INFO 38484 --- [eduler_Worker-3] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:02.000 INFO 38484 --- [eduler_Worker-3] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:02 CST 2021 2021-10-01 13:09:03.000 INFO 38484 --- [eduler_Worker-4] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:03.001 INFO 38484 --- [eduler_Worker-4] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:03 CST 2021 2021-10-01 13:09:04.001 INFO 38484 --- [eduler_Worker-5] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:04.001 INFO 38484 --- [eduler_Worker-5] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:04 CST 2021 2021-10-01 13:09:05.002 INFO 38484 --- [eduler_Worker-6] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:05.003 INFO 38484 --- [eduler_Worker-6] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:05 CST 2021 2021-10-01 13:09:06.000 INFO 38484 --- [eduler_Worker-7] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:06.001 INFO 38484 --- [eduler_Worker-7] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:06 CST 2021 2021-10-01 13:09:07.002 INFO 38484 --- [eduler_Worker-8] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:07.002 INFO 38484 --- [eduler_Worker-8] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:07 CST 2021 2021-10-01 13:09:08.002 INFO 38484 --- [eduler_Worker-9] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:08.003 INFO 38484 --- [eduler_Worker-9] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:08 CST 2021 2021-10-01 13:09:09.000 INFO 38484 --- [duler_Worker-10] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:09.000 INFO 38484 --- [duler_Worker-10] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:09 CST 2021 2021-10-01 13:09:10.001 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:10.002 INFO 38484 --- [eduler_Worker-1] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:10 CST 2021 2021-10-01 13:09:11.014 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : param, key:msg, value:Hello Quartz 2021-10-01 13:09:11.014 INFO 38484 --- [eduler_Worker-2] t.pdai.springboot.quartz.job.HelloJob : Hello Job执行时间: Wed Oct 27 13:09:11 CST 2021
实现案例 - 分布式方式
本例将展示quartz实现基于数据库的分布式任务管理,和控制job生命周期。
整体项目结构如下:
后端实现
pom.xml
org.springframework.boot spring-boot-starter-parent 2.5.3 4.0.0 tech.pdai 423-springboot-demo-schedule-quartz-cluster 1.0-SNAPSHOT 8 8 org.springframework.boot spring-boot-starter-quartz org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java 5.1.42 runtime org.springframework.boot spring-boot-starter-test test org.projectlombok lombok 1.18.20 true com.github.pagehelper pagehelper 5.0.0
创建Schema
需要提前在MySQL中创建schema: quartz_jobs
# 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; # DROP TABLE IF EXISTS QRTZ_TASK_HISTORY; CREATE TABLE QRTZ_JOB_DETAILS( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) 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(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) 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(200) 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(200) 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(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) 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(200) NULL, JOB_GROUP VARCHAR(200) 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(200) 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 TABLE QRTZ_TASK_HISTORY ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_ID VARCHAR(200) NOT NULL, FIRE_ID VARCHAR(95) NOT NULL, TASK_NAME VARCHAR(200) NULL, TASK_GROUP VARCHAR(200) NULL, FIRED_TIME BIGINT(13) NULL, FIRED_WAY VARCHAR(8) NULL, COMPLETE_TIME BIGINT(13) NULL, EXPEND_TIME BIGINT(13) NULL, REFIRED INT NULL, EXEC_STATE VARCHAR(10) NULL, LOG TEXT NULL, PRIMARY KEY (FIRE_ID) )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); CREATE INDEX IDX_QRTZ_TK_S ON QRTZ_TASK_HISTORY(SCHED_NAME); commit;
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/quartz_jobs?useUnicode=true&useSSL=false
username: root
password: xxxxxxxx
driver-class-name: com.mysql.jdbc.Driver
quartz:
#相关属性配置
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#数据库方式
job-store-type: jdbc
定义JobDetails实体
/**
* @author pdai
*
*/
@Data
public class JobDetails{
private String cronExpression;
private String jobClassName;
private String triggerGroupName;
private String triggerName;
private String jobGroupName;
private String jobName;
private Date nextFireTime;
private Date previousFireTime;
private Date startTime;
private String timeZone;
private String status;
}
Job管理类
package tech.pdai.springboot.quartz.cluster.manager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import tech.pdai.springboot.quartz.cluster.entity.JobDetails;
/**
* @author pdai
*/
@Component
public class QuartzManager {
@Autowired
private Scheduler sched;
/**
* 创建or更新任务,存在则更新不存在创建
*
* @param jobClass 任务类
* @param jobName 任务名称
* @param jobGroupName 任务组名称
* @param jobCron cron表达式
*/
public void addOrUpdateJob(Class extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger==null) {
addJob(jobClass, jobName, jobGroupName, jobCron);
} else {
if (trigger.getCronExpression().equals(jobCron)) {
return;
}
updateJob(jobName, jobGroupName, jobCron);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 增加一个job
*
* @param jobClass 任务实现类
* @param jobName 任务名称
* @param jobGroupName 任务组名
* @param jobCron cron表达式(如:0/5 * * * * ? )
*/
public void addJob(Class extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobCron) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
.withSchedule(CronScheduleBuilder.cronSchedule(jobCron)).startNow().build();
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* @param jobClass
* @param jobName
* @param jobGroupName
* @param jobTime
*/
public void addJob(Class extends Job> jobClass, String jobName, String jobGroupName, int jobTime) {
addJob(jobClass, jobName, jobGroupName, jobTime, -1);
}
public void addJob(Class extends Job> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes) {
try {
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)// 任务名称和组构成任务key
.build();
// 使用simpleTrigger规则
Trigger trigger;
if (jobTimes < 0) {
trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
.startNow().build();
} else {
trigger = TriggerBuilder
.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
.repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
.startNow().build();
}
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public void updateJob(String jobName, String jobGroupName, String jobTime) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build();
// 重启触发器
sched.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 删除任务一个job
*
* @param jobName 任务名称
* @param jobGroupName 任务组名
*/
public void deleteJob(String jobName, String jobGroupName) {
try {
sched.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
sched.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
sched.deleteJob(new JobKey(jobName, jobGroupName));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 暂停一个job
*
* @param jobName
* @param jobGroupName
*/
public void pauseJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 恢复一个job
*
* @param jobName
* @param jobGroupName
*/
public void resumeJob(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.resumeJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 立即执行一个job
*
* @param jobName
* @param jobGroupName
*/
public void runAJobNow(String jobName, String jobGroupName) {
try {
JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
sched.triggerJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public PageInfo queryAllJobBean(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List jobList = null;
try {
GroupMatcher matcher = GroupMatcher.anyJobGroup();
Set jobKeys = sched.getJobKeys(matcher);
jobList = new ArrayList<>();
for (JobKey jobKey : jobKeys) {
List extends Trigger> triggers = sched.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
JobDetails jobDetails = new JobDetails();
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
jobDetails.setCronExpression(cronTrigger.getCronExpression());
jobDetails.setTimeZone(cronTrigger.getTimeZone().getDisplayName());
}
jobDetails.setTriggerGroupName(trigger.getKey().getName());
jobDetails.setTriggerName(trigger.getKey().getGroup());
jobDetails.setJobGroupName(jobKey.getGroup());
jobDetails.setJobName(jobKey.getName());
jobDetails.setStartTime(trigger.getStartTime());
jobDetails.setJobClassName(sched.getJobDetail(jobKey).getJobClass().getName());
jobDetails.setNextFireTime(trigger.getNextFireTime());
jobDetails.setPreviousFireTime(trigger.getPreviousFireTime());
jobDetails.setStatus(sched.getTriggerState(trigger.getKey()).name());
jobList.add(jobDetails);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
return new PageInfo<>(jobList);
}
/**
* 获取所有计划中的任务列表
*
* @return
*/
public List Job控制器接口
package tech.pdai.springboot.quartz.cluster.controller;
import java.util.HashMap;
import java.util.Map;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import tech.pdai.springboot.quartz.cluster.entity.JobDetails;
import tech.pdai.springboot.quartz.cluster.manager.QuartzManager;
/**
* @author pdai
*/
@RestController
@RequestMapping(value = "/job")
public class JobController {
@Autowired
private QuartzManager qtzManager;
@SuppressWarnings("unchecked")
private static Class extends QuartzJobBean> getClass(String classname) throws Exception {
Class> class1 = Class.forName(classname);
return (Class extends QuartzJobBean>) class1;
}
/**
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value = "/addjob")
public void addjob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
}
/**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/pausejob")
public void pausejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.pauseJob(jobClassName, jobGroupName);
}
/**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/resumejob")
public void resumejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.resumeJob(jobClassName, jobGroupName);
}
/**
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value = "/reschedulejob")
public void rescheduleJob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
qtzManager.addOrUpdateJob(getClass(jobClassName), jobClassName, jobGroupName, cronExpression);
}
/**
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/deletejob")
public void deletejob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
qtzManager.deleteJob(jobClassName, jobGroupName);
}
/**
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping(value = "/queryjob")
public Map queryjob(@RequestParam(value = "pageNum") Integer pageNum,
@RequestParam(value = "pageSize") Integer pageSize) {
PageInfo jobAndTrigger = qtzManager.queryAllJobBean(pageNum, pageSize);
Map map = new HashMap();
map.put("JobAndTrigger", jobAndTrigger);
map.put("number", jobAndTrigger.getTotal());
return map;
}
}
定义具体的Job
package tech.pdai.springboot.quartz.cluster.job;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
@Slf4j
public class HelloJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// get parameters
context.getJobDetail().getJobDataMap().forEach(
(k, v) -> log.info("param, key:{}, value:{}", k, v)
);
// your logics
log.info("Hello Job执行时间: " + new Date());
}
}
前端实现
简单用VueJS 写个页面测试
QuartzDemo <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script> <script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script><script> var vue = new Vue({ el:"#test", data: { //表格当前页数据 tableData: [], //请求的URL url:"job/queryjob", //默认每页数据量 pagesize: 10, //当前页码 currentPage: 1, //查询的页码 start: 1, //默认数据总数 totalCount: 1000, //添加对话框默认可见性 dialogFormVisible: false, //修改对话框默认可见性 updateFormVisible: false, //提交的表单 form: { jobName: "", jobGroup: "", cronExpression: "", }, updateform: { jobName: "", jobGroup: "", cronExpression: "", }, }, methods: { //从服务器读取数据 loadData: function(pageNum, pageSize){ this.$http.get("job/queryjob?" + "pageNum=" + pageNum + "&pageSize=" + pageSize).then(function(res){ console.log(res) this.tableData = res.body.JobAndTrigger.list; this.totalCount = res.body.number; },function(){ console.log("failed"); }); }, //单行删除 handleDelete: function(index, row) { this.$http.post("job/deletejob",{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){ this.loadData( this.currentPage, this.pagesize); },function(){ console.log("failed"); }); }, //暂停任务 handlePause: function(index, row){ this.$http.post("job/pausejob",{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){ this.loadData( this.currentPage, this.pagesize); },function(){ console.log("failed"); }); }, //恢复任务 handleResume: function(index, row){ this.$http.post("job/resumejob",{"jobClassName":row.jobName,"jobGroupName":row.jobGroupName},{emulateJSON: true}).then(function(res){ this.loadData( this.currentPage, this.pagesize); },function(){ console.log("failed"); }); }, //搜索 search: function(){ this.loadData(this.currentPage, this.pagesize); }, //弹出对话框 handleadd: function(){ this.dialogFormVisible = true; }, //添加 add: function(){ this.$http.post("job/addjob",{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){ this.loadData(this.currentPage, this.pagesize); this.dialogFormVisible = false; },function(){ console.log("failed"); }); }, //更新 handleUpdate: function(index, row){ console.log(row) this.updateFormVisible = true; this.updateform.jobName = row.jobClassName; this.updateform.jobGroup = row.jobGroupName; }, //更新任务 update: function(){ this.$http.post ("job/reschedulejob", {"jobClassName":this.updateform.jobName, "jobGroupName":this.updateform.jobGroup, "cronExpression":this.updateform.cronExpression },{emulateJSON: true} ).then(function(res){ this.loadData(this.currentPage, this.pagesize); this.updateFormVisible = false; },function(){ console.log("failed"); }); }, //每页显示数据量变更 handleSizeChange: function(val) { this.pagesize = val; this.loadData(this.currentPage, this.pagesize); }, //页码变更 handleCurrentChange: function(val) { this.currentPage = val; this.loadData(this.currentPage, this.pagesize); }, }, }); //载入数据 vue.loadData(vue.currentPage, vue.pagesize); </script>查询 添加
暂停 恢复 删除 修改 取 消 确 定 取 消 确 定
测试效果
(PS: 这里的任务名称需要改成你自己的完整类名称)
展示正在运行的Jobs:
增加新的Job:
Jobs持久化在数据库:
以上就是SpringBoot集成quartz实现定时任务详解的详细内容,更多关于SpringBoot quartz定时任务的资料请关注脚本之家其它相关文章!
X 关闭
X 关闭
- 1转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 2充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 3好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 4名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?
- 5亚马逊云科技成立量子网络中心致力解决量子计算领域的挑战
- 6京东绿色建材线上平台上线 新增用户70%来自下沉市场
- 7网红淘品牌“七格格”chuu在北京又开一家店 潮人新宠chuu能红多久
- 8市场竞争加剧,有车企因经营不善出现破产、退网、退市
- 9北京市市场监管局为企业纾困减负保护经济韧性
- 10市场监管总局发布限制商品过度包装标准和第1号修改单

