Quartz 分布式调度方案
1.项目结构
quartz-module
└─src
└─main
├─java
│ └─com
│ └─yan
│ └─task
│ └─quartz
│ ├─config
│ ├─dstributed
│ ├─enums
│ └─job
└─resources
└─db
2.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yan</groupId>
<artifactId>quartz-module</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>quartz-module</name>
<description>quartz-module</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.yan</groupId>-->
<!-- <artifactId>framework-redis</artifactId>-->
<!-- <version>0.0.1-SNAPSHOT</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- orm Mybatis-Plus 和 JPA 二选一-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
<!-- </dependency>-->
<!--Mybatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.具体实现
3.1.quartz.sql
create database quartz;
use quartz;
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(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)
);
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)
);
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)
);
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(200) 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)
);
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)
);
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),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
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)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
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)
);
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)
);
CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
3.2 配置
3.2.1 application.yml
spring:
task:
application:
name: quartz-module
datasource:
url: jdbc:mysql://127.0.0.1:3306/quartz?useSSL=false&serverTimezone=UTC
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
max-active: 1000
max-idle: 20
min-idle: 5
initial-size: 10
# 是否使用properties作为数据存储(以下配置与quartz.properties二选一即可)
org:
quartz:
jobStore:
useProperties: false
# 数据库中表的命名前缀
tablePrefix: QRTZ_
# 是否是一个集群,是不是分布式的任务
isClustered: true
# 集群检查周期,单位为毫秒,可以自定义缩短时间。当某一个节点宕机的时候,其他节点等待多久后开始执行任务
clusterCheckinInterval: 5000
# 单位为毫秒,集群中的节点退出后,再次检查进入的时间间隔
misfireThreshold: 60000
# 事务隔离级别
txIsolationLevelReadCommitted: true
# 存储的事务管理类型
# class: org:quartz:impl.jdbcjobstore.JobStoreTX
class: org:springframework.scheduling.quartz.LocalDataSourceJobStore
# 使用的Delegate类型
driverDelegateClass: org:quartz:impl.jdbcjobstore.StdJDBCDelegate
# dataSource: quartzDataSource
# 集群的命名,一个集群要有相同的命名
scheduler:
instanceName: ClusterQuartz
# 节点的命名,可以自定义。AUTO代表自动生成
instanceId: AUTO
# rmi远程协议是否发布
rmi.export: false
# rmi远程协议代理是否创建
rmi.proxy: false
# 是否使用用户控制的事务环境触发执行任务
wrapJobExecutionInUserTransaction: false
3.2.2 quartz.properties
# 是否使用properties作为数据存储
org.quartz.jobStore.useProperties=false
# 数据库中表的命名前缀
org.quartz.jobStore.tablePrefix=QRTZ_
# 是否是一个集群,是不是分布式的任务
org.quartz.jobStore.isClustered=true
# 集群检查周期,单位为毫秒,可以自定义缩短时间。当某一个节点宕机的时候,其他节点等待多久后开始执行任务
org.quartz.jobStore.clusterCheckinInterval=5000
# 单位为毫秒,集群中的节点退出后,再次检查进入的时间间隔
org.quartz.jobStore.misfireThreshold=60000
# 事务隔离级别
org.quartz.jobStore.txIsolationLevelReadCommitted=true
# 存储的事务管理类型
#org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore
# 使用的Delegate类型
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.dataSource: quartzDataSource
# 集群的命名,一个集群要有相同的命名
org.quartz.scheduler.instanceName=ClusterQuartz
# 节点的命名,可以自定义。AUTO代表自动生成
org.quartz.scheduler.instanceId=AUTO
# rmi远程协议是否发布
org.quartz.scheduler.rmi.export=false
# rmi远程协议代理是否创建
org.quartz.scheduler.rmi.proxy=false
# 是否使用用户控制的事务环境触发执行任务
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
3.3 代码实现
3.3.1 枚举
com.yan.task.quartz.enums
CronTemplate
package com.yan.task.quartz.enums;
import cn.hutool.core.date.DatePattern;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.concurrent.TimeUnit;
/**
* @Author yan
* @DateTime 2024/6/20 22:52:03
* @Description
*/
@Getter
@AllArgsConstructor
public enum CronTemplate {
MINUTE("0 0/%s * * * ?",TimeUnit.MINUTES, DatePattern.UTC_SIMPLE_PATTERN.replace(":ss", "")),
HOUR("0 0 0/%s * * ?",TimeUnit.HOURS, DatePattern.UTC_SIMPLE_PATTERN.replace(":mm:ss", "")),
CLOCK("0 0 %s * * ?",null, DatePattern.UTC_SIMPLE_PATTERN.replace(":mm:ss", ""));
private String cronTemplate;
private TimeUnit timeUnit;
private String datePattern;
}
QuartzName
package com.yan.task.quartz.enums;
/**
* @author Mysteriousman
*/
public enum QuartzName {
/**
* 每分钟
*/
MINUTE_1,
/**
* 每5分钟
*/
MINUTE_5,
/**
* 每10分钟
*/
MINUTE_10,
/**
* 每15分钟
*/
MINUTE_15,
/**
* 每30分钟
*/
MINUTE_30,
/**
* 每1小时
*/
HOUR_1,
/**
* 每2小时
*/
HOUR_2,
/**
* 每3小时
*/
HOUR_3,
/**
* 凌晨0点
*/
CLOCK_0,
/**
* 凌晨2点
*/
CLOCK_2,
/**
* 上午8点
*/
CLOCK_8,
/**
* 上午9点
*/
CLOCK_9,
/**
* 上午10点
*/
CLOCK_10,
/**
* 上午12点
*/
CLOCK_12,
/**
* 下午15点
*/
CLOCK_15,
/**
* 下午17点
*/
CLOCK_17,
/**
* 下午18点
*/
CLOCK_18,
/**
* 晚上21点
*/
CLOCK_21,
/**
* 晚上23点
*/
CLOCK_23;
}
QuartzGroup
package com.yan.task.quartz.enums;
/**
* @author yan
*/
public enum QuartzGroup {
/**
* 当前默认组名
*/
DEFAULT
}
3.3.2 配置
com.yan.task.quartz.config
AutowiringSpringBeanJobFactory
package com.yan.task.quartz.config;
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
/**
* @Author yan
* @DateTime 2024/6/23 15:45:32
* @Description
*/
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(AutowiringSpringBeanJobFactory.class);
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
//LOG.info("create job instance");
beanFactory.autowireBean(job);
return job;
}
}
SchedulerConfig
package com.yan.task.quartz.config;
import cn.hutool.extra.spring.SpringUtil;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleTrigger;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* @Author yan
* @DateTime 2024/6/23 14:39:51
* @Description
*/
@Configuration
public class SchedulerConfig {
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
//@Bean //使用quartz.properties需开启注释
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
ClassPathResource location = new ClassPathResource("quartz.properties");
propertiesFactoryBean.setLocation(location);
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
return factoryBean;
}
public static JobDetailFactoryBean createJobDetail(Class jobClass) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
factoryBean.setDurability(true);
return factoryBean;
}
@Resource
private DataSource dataSource;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(jobFactory);
// 覆盖已存在的任务
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setDataSource(dataSource);
// 延迟启动
schedulerFactoryBean.setStartupDelay(1);
//schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
return schedulerFactoryBean;
}
@Bean
public Scheduler scheduler() {
JobFactory jobFactory = SpringUtil.getBean(JobFactory.class);
Scheduler scheduler = schedulerFactoryBean(jobFactory).getScheduler();
return scheduler;
}
}
QuartzObject
package com.yan.task.quartz.config;
import com.yan.task.quartz.enums.CronTemplate;
import com.yan.task.quartz.enums.QuartzGroup;
import com.yan.task.quartz.enums.QuartzName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.quartz.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class QuartzObject {
private Class<Job> clockJobClass;
private QuartzGroup quartzGroup;
private QuartzName quartzName;
private CronTemplate cronTemplate;
private String value;
private JobKey jobKey;
private TriggerKey triggerKey;
private CronScheduleBuilder scheduleBuilder;
private JobDetail jobDetail;
private Trigger trigger;
}
QuartzConfig
package com.yan.task.quartz.config;
import com.yan.task.quartz.enums.CronTemplate;
import com.yan.task.quartz.enums.QuartzGroup;
import com.yan.task.quartz.enums.QuartzName;
import com.yan.task.quartz.job.Clock0Job;
import com.yan.task.quartz.job.Minute1Job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Yao
* @date 2024/5/28 17:52
*/
@Configuration
@Slf4j
public class QuartzConfig {
public static Map<QuartzName, QuartzObject> QUARTZ_OBJECT_MAP = new HashMap<>();
public static Map<Class<Job>,QuartzName> JOB_CLASS_QUARTZ_NAME_MAP = new HashMap<>();
public static List<QuartzObject> quartzObjectList = new ArrayList<>();
private static final Map<QuartzName, JobKey> JOB_KEY_MAP = new HashMap<>();
private static final Map<QuartzName, CronScheduleBuilder> CRON_SCHEDULE_MAP = new HashMap<>();
private static final Map<QuartzName, TriggerKey> TRIGGER_KEY_MAP = new EnumMap<>(QuartzName.class);
private Map<QuartzName, JobDetail> JOB_DETAIL_MAP = new HashMap<>();
static {
Map<QuartzName, Class> quartzClassMap = new LinkedHashMap<>();
quartzClassMap.put(QuartzName.MINUTE_1, Minute1Job.class);
quartzClassMap.put(QuartzName.CLOCK_0, Clock0Job.class);
log.info("~~~~~~~~~~~~~~~~~~QuartzConfig init~~~~~~~~~~~~~~~~~~");
QuartzGroup quartzGroup = QuartzGroup.DEFAULT;
String group = quartzGroup.name();
Arrays.stream(QuartzName.values()).collect(Collectors.toList()).stream().forEach(quartzName -> {
String name = quartzName.name();
QuartzObject quartzObject = getQuartzObject(quartzName);
JobKey jobKey = JobKey.jobKey(name, group);
TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
//JOB_KEY_MAP.put(quartzName, jobKey);
//TRIGGER_KEY_MAP.put(quartzName, triggerKey);
String[] split = name.split("_");
CronTemplate cronTemplate = CronTemplate.valueOf(split[0]);
String value = split[1];
String cronExpression = String.format(cronTemplate.getCronTemplate(), value);
CronScheduleBuilder scheduleBuilder = buildCronScheduleBuilder(cronExpression);
//CRON_SCHEDULE_MAP.put(quartzName, scheduleBuilder);
quartzObject
.setQuartzGroup(quartzGroup)
.setQuartzName(quartzName)
.setCronTemplate(cronTemplate)
.setValue(value)
.setJobKey(jobKey)
.setTriggerKey(triggerKey)
.setScheduleBuilder(scheduleBuilder);
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject);
});
quartzClassMap.keySet().stream().forEach(quartzName -> {
setClockJobClass(quartzName,quartzClassMap.get(quartzName));
});
List<QuartzObject> quartzObjects = QUARTZ_OBJECT_MAP.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toList());
quartzObjectList.addAll(quartzObjects);
quartzObjectList.stream().forEach(quartzObject -> {
JOB_CLASS_QUARTZ_NAME_MAP.put(quartzObject.getClockJobClass(),quartzObject.getQuartzName());
});
log.info("~~~~~~~~~~~~~~~~~~QuartzConfig init end~~~~~~~~~~~~~~~~~~");
}
public static QuartzObject getQuartzObject(QuartzName quartzName) {
QuartzObject quartzObject = QUARTZ_OBJECT_MAP.get(quartzName);
if (quartzObject == null) {
quartzObject = new QuartzObject();
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject);
}
return QUARTZ_OBJECT_MAP.get(quartzName);
}
public static QuartzObject setClockJobClass(QuartzName quartzName, Class clockJobClass) {
QuartzObject quartzObject = getQuartzObject(quartzName);
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject.setClockJobClass(clockJobClass));
return QUARTZ_OBJECT_MAP.get(quartzName);
}
public static CronScheduleBuilder buildCronScheduleBuilder(String cronExpression) {
return CronScheduleBuilder.cronSchedule(cronExpression);
}
public JobDetail buildNewJobDetail(Class clockJob, JobKey jobKey) {
return JobBuilder.newJob(clockJob).withIdentity(jobKey).storeDurably().build();
}
public Trigger buildNewTrigger(CronScheduleBuilder scheduleBuilder, JobKey jobKey, TriggerKey triggerKey) {
return TriggerBuilder.newTrigger().forJob(jobKey).withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
}
public JobDetail buildNewJobDetail(Class clockJob, QuartzName quartzName) {
QuartzObject quartzObject = getQuartzObject(quartzName);
JobKey jobKey = quartzObject.getJobKey();
JobDetail jobDetail = buildNewJobDetail(clockJob, jobKey);
//JOB_DETAIL_MAP.put(quartzName, jobDetail);
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject.setJobDetail(jobDetail));
return jobDetail;
}
public JobDetail buildNewJobDetail(QuartzName quartzName) {
QuartzObject quartzObject = getQuartzObject(quartzName);
Class clockJobClass = quartzObject.getClockJobClass();
JobKey jobKey = quartzObject.getJobKey();
JobDetail jobDetail = buildNewJobDetail(clockJobClass, jobKey);
//JOB_DETAIL_MAP.put(quartzName, jobDetail);
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject.setJobDetail(jobDetail));
return jobDetail;
}
public Trigger buildNewTrigger(QuartzName quartzName) {
QuartzObject quartzObject = getQuartzObject(quartzName);
CronScheduleBuilder build = quartzObject.getScheduleBuilder();
JobKey jobKey = quartzObject.getJobKey();
TriggerKey triggerKey = quartzObject.getTriggerKey();
//CronScheduleBuilder build = CRON_SCHEDULE_MAP.get(quartzName);
//JobKey jobKey = JOB_KEY_MAP.get(quartzName);
//TriggerKey triggerKey = TRIGGER_KEY_MAP.get(quartzName);
Trigger trigger = buildNewTrigger(build, jobKey, triggerKey);
QUARTZ_OBJECT_MAP.put(quartzName, quartzObject.setTrigger(trigger));
return trigger;
}
public Trigger buildNewTrigger(JobDetail jobDetail, QuartzName quartzName) {
QuartzObject quartzObject = getQuartzObject(quartzName);
CronScheduleBuilder build = quartzObject.getScheduleBuilder();
//JobKey jobKey = quartzObject.getJobKey();
TriggerKey triggerKey = quartzObject.getTriggerKey();
//CronScheduleBuilder build = CRON_SCHEDULE_MAP.get(quartzName);
//JobKey jobKey = JOB_KEY_MAP.get(quartzName);
//TriggerKey triggerKey = TRIGGER_KEY_MAP.get(quartzName);
return TriggerBuilder.newTrigger().forJob(jobDetail).withIdentity(triggerKey).withSchedule(build).build();
}
@Bean
public JobDetail clock0JobDetail() {
return buildNewJobDetail(QuartzName.CLOCK_0);
}
@Bean
public Trigger clock0Trigger() {
return buildNewTrigger(QuartzName.CLOCK_0);
}
@Bean
public JobDetail minute1JobDetail() {
log.info("~~~~~~~~~~~~~~~~~~minute1JobDetail~~~~~~~~~~~~~~~~~~");
return buildNewJobDetail(QuartzName.MINUTE_1);
}
@Bean
public Trigger minute1Trigger() {
log.info("~~~~~~~~~~~~~~~~~~minute1Trigger~~~~~~~~~~~~~~~~~~");
return buildNewTrigger(QuartzName.MINUTE_1);
}
}
JobStartupRunner
package com.yan.task.quartz.config;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author yan
* @DateTime 2024/6/23 14:42:09
* @Description
*/
@Component
@Slf4j
public class JobStartupRunner implements CommandLineRunner {
@Resource
SchedulerConfig schedulerConfig;
@Override
public void run(String... args) throws Exception {
Scheduler scheduler;
try {
scheduler = schedulerConfig.scheduler();
List<QuartzObject> quartzObjectList = QuartzConfig.quartzObjectList;
quartzObjectList.stream().forEach(quartzObject -> {
TriggerKey triggerKey = quartzObject.getTriggerKey();
JobKey jobKey = quartzObject.getJobKey();
Trigger trigger = quartzObject.getTrigger();
JobDetail jobDetail = quartzObject.getJobDetail();
try {
CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (null == cronTrigger && jobDetail != null && trigger != null) {
scheduler.scheduleJob(jobDetail, trigger);
log.info("==> Quartz 创建了job: {} <==", jobDetail.getKey());
} else {
if (jobDetail != null) {
log.info("==> {} job已存在 <==", jobDetail.getKey());
} else {
log.info("==> {} job未初始化 <==",jobKey);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
scheduler.start();
} catch (Exception e) {
throw e;
}
}
}
3.3.3 禁止并发执行
DistributedJob
package com.yan.task.quartz.dstributed;
import cn.hutool.core.date.DatePattern;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* @Author yan
* @DateTime 2024/6/23 14:56:39
* @Description 分布式 Job
*/
// 持久化
@PersistJobDataAfterExecution
// 禁止并发执行
@DisallowConcurrentExecution
public class DistributedJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
Logger log = LoggerFactory.getLogger(this.getClass());
String taskName = context.getJobDetail().getJobDataMap().getString("name");
log.info("===> Quartz job, time:{"+ DateTimeFormatter.ofPattern(DatePattern
.NORM_DATETIME_PATTERN).format(LocalDateTime.now()) +"} ,name:{"+taskName+"} <===");
}
}
job 事例
package com.yan.task.quartz.job;
import com.yan.task.quartz.dstributed.DistributedJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author Yao
* @date 2024/3/4 17:25
*/
@Slf4j
@Component
public class Clock0Job extends DistributedJob {
//@Override
//public void executeDistributed(JobExecutionContext var1) throws JobExecutionException {
// log.info("~~~~~~~~~~~~~~~~~~~~~~~~~Clock0Job~~~~~~~~~~~~~~~~~~~~~~~~~");
//}
//@Override
//public void execute(JobExecutionContext context) throws JobExecutionException {
// log.info("~~~~~~~~~~~~~~~~~~~~~~~~~Clock0Job~~~~~~~~~~~~~~~~~~~~~~~~~");
//}
}
package com.yan.task.quartz.job;
import cn.hutool.extra.spring.SpringUtil;
import com.yan.task.quartz.dstributed.DistributedJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Component
public class Minute1Job extends DistributedJob {
//@Override
//public void executeDistributed(JobExecutionContext var1) throws JobExecutionException {
// log.info("~~~~~~~~~~~~~~~~~~~~~~~~~Minute1Job~~~~~~~~~~~~~~~~~~~~~~~~~");
//}
//@Override
//public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// log.info("~~~~~~~~~~~~~~~~~~~~~~~~~Minute1Job~~~~~~~~~~~~~~~~~~~~~~~~~");
//}
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
ThreadPoolTaskExecutor executor = SpringUtil.getBean(ThreadPoolTaskExecutor.class);
Minute1Job.super.executeInternal(context);
log.info("~~~~~~~~~~~~~~~~~~~~~~~~~{}~~~~~~~~~~~~~~~~~~~~~~~~~",this.getClass().getSimpleName());
//CustThreadPoolTaskExecutor runAsync = new CustThreadPoolTaskExecutor().setCustThreadNamePrefix("runAsync");
CompletableFuture.runAsync(()->{
log.info("===> {} <===","runAsync");
}, executor);
}
}
3.3.4 启动类
package com.yan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class QuartzModuleApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzModuleApplication.class, args);
}
}