文章共 2625 字,阅读完预计需要 4 分钟 23 秒。文章篇幅适中,可以放心阅读。
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.JobStoreTXorg.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@AllArgsConstructorpublic 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 */@Configurationpublic 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@Slf4jpublic 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@Slf4jpublic 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// 禁止并发执行@DisallowConcurrentExecutionpublic 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@Componentpublic 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@Componentpublic 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; @SpringBootApplicationpublic class QuartzModuleApplication { public static void main(String[] args) { SpringApplication.run(QuartzModuleApplication.class, args); } }