上次本身搭建Quartz已是几年前的事了,此次项目中须要定时任务,须要支持集群部署,想到比较轻量级的定时任务框架就是Quartz,因而来一波。java
经过搜索引擎很容易找到其官网,来到Document的页面,当前版本是2.2.x。mysql
经过Maven引入所需的包:spring
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency>
经过quartz.properties设置相关配置:sql
# 线程调度器实例名 org.quartz.scheduler.instanceName = quartzScheduler # 线程池的线程数,即最多3个任务同时跑 org.quartz.threadPool.threadCount = 3 # 使用内存存储任务和触发器等信息 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
定义任务,如MySimpleJob,再初始化Scheduler,最后将任务和出发器注册到Scheduler上:数据库
package com.nicchagil.exercise.quartz.springbootquartzexercise; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; public class QuartzBoot { private static Logger logger = LoggerFactory.getLogger(QuartzBoot.class); public static void main(String[] args) { try { // 获取调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 开启调度器 scheduler.start(); // 注册一个示例任务和触发器 registerJobAndTrigger(scheduler); // scheduler.shutdown(); } catch (SchedulerException se) { logger.error("调度器初始化异常", se); } } /** * 注册一个任务和触发器 */ public static void registerJobAndTrigger(Scheduler scheduler) { JobDetail job = JobBuilder.newJob(MySimpleJob.class) .withIdentity("mySimpleJob", "simpleGroup") .build(); Trigger trigger = org.quartz.TriggerBuilder.newTrigger() .withIdentity("simpleTrigger", "simpleGroup") .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(10) .repeatForever()) .build(); try { scheduler.scheduleJob(job, trigger); } catch (SchedulerException e) { logger.error("注册任务和触发器失败", e); } } /** * 简单的任务 */ public static class MySimpleJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { logger.info("哇真的执行了"); } } }
启动日志,你能够看到任务按照指定的触发器跑:缓存
13:31:28.759 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor 13:31:28.828 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl 13:31:28.828 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.2.3 created. 13:31:28.831 [main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized. 13:31:28.833 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.3) '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 3 threads. Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. 13:31:28.833 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'quartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties' 13:31:28.833 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.2.3 13:31:28.834 [main] INFO org.quartz.core.QuartzScheduler - Scheduler quartzScheduler_$_NON_CLUSTERED started. 13:31:28.834 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers 13:31:28.853 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers 13:31:28.860 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'simpleGroup.mySimpleJob', class=com.nicchagil.exercise.quartz.springbootquartzexercise.QuartzBoot$MySimpleJob 13:31:28.869 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers 13:31:28.871 [quartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job simpleGroup.mySimpleJob 13:31:28.871 [quartzScheduler_Worker-1] INFO com.nicchagil.exercise.quartz.springbootquartzexercise.QuartzBoot - 哇真的执行了 13:31:38.842 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'simpleGroup.mySimpleJob', class=com.nicchagil.exercise.quartz.springbootquartzexercise.QuartzBoot$MySimpleJob 13:31:38.842 [quartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job simpleGroup.mySimpleJob 13:31:38.842 [quartzScheduler_Worker-2] INFO com.nicchagil.exercise.quartz.springbootquartzexercise.QuartzBoot - 哇真的执行了
查看StdSchedulerFactory.getDefaultScheduler()
,会发现用的是new StdSchedulerFactory()
,在initialize()
方法能够看到默认从指定配置的文件或quartz.properties
读取配置:springboot
String requestedFile = System.getProperty("org.quartz.properties"); String propFileName = requestedFile != null ? requestedFile : "quartz.properties";
在调用scheduler.start()
启动调度器后,可使用scheduler.standby();
将调度器转为待机状态,此状态下任务和触发器不会被触发。并发
另外,可使用scheduler.shutdown()
关闭调度器,是不可逆的,即调用后是不能够从新开始的。
它的参数不一样,意义有所不一样:框架
scheduler.shutdown()
=scheduler.shutdown(false)
,方法会立刻返回,正在执行的任务会继续执行scheduler.shutdown(true)
,知道正在执行的任务执行完成才返回在getScheduler()
能够看到使用SchedulerRepository
缓存了Scheduler
,使用的是HashMap<String, Scheduler>
:ide
SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(this.getSchedulerName());
首先,quartz.properties与以前的基本一致:
# 线程调度器实例名 org.quartz.scheduler.instanceName = quartzScheduler # 线程池的线程数,即最多3个任务同时跑 org.quartz.threadPool.threadCount = 3 # 使用内存存储任务和触发器等信息 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
因会与Spring Scheduling集成,咱们按照要求定义任务,须要特别注意的是@Component
和@EnableScheduling
:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz.job; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.stereotype.Component; @Component @EnableScheduling public class MyFirstExerciseJob { private Logger logger = LoggerFactory.getLogger(this.getClass()); public void myJobBusinessMethod() { this.logger.info("哇被触发了哈哈哈哈哈"); } }
相似的方式,能够定义MySecondExerciseJob
。
注册任务和触发器:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz; import com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz.job.MyFirstExerciseJob; import com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz.job.MySecondExerciseJob; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; @Configuration public class QuartzJobConfig { /** * 方法调用任务明细工厂Bean */ @Bean(name = "myFirstExerciseJobBean") public MethodInvokingJobDetailFactoryBean myFirstExerciseJobBean(MyFirstExerciseJob myFirstExerciseJob) { MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); jobDetail.setConcurrent(false); // 是否并发 jobDetail.setName("general-myFirstExerciseJob"); // 任务的名字 jobDetail.setGroup("general"); // 任务的分组 jobDetail.setTargetObject(myFirstExerciseJob); // 被执行的对象 jobDetail.setTargetMethod("myJobBusinessMethod"); // 被执行的方法 return jobDetail; } /** * 表达式触发器工厂Bean */ @Bean(name = "myFirstExerciseJobTrigger") public CronTriggerFactoryBean myFirstExerciseJobTrigger(@Qualifier("myFirstExerciseJobBean") MethodInvokingJobDetailFactoryBean myFirstExerciseJobBean) { CronTriggerFactoryBean tigger = new CronTriggerFactoryBean(); tigger.setJobDetail(myFirstExerciseJobBean.getObject()); tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式 tigger.setName("general-myFirstExerciseJobTrigger"); return tigger; } /** * 方法调用任务明细工厂Bean */ @Bean(name = "mySecondExerciseJobBean") public MethodInvokingJobDetailFactoryBean mySecondExerciseJobBean(MySecondExerciseJob mySecondExerciseJob) { MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); jobDetail.setConcurrent(false); // 是否并发 jobDetail.setName("general-mySecondExerciseJob"); // 任务的名字 jobDetail.setGroup("general"); // 任务的分组 jobDetail.setTargetObject(mySecondExerciseJob); // 被执行的对象 jobDetail.setTargetMethod("myJobBusinessMethod"); // 被执行的方法 return jobDetail; } /** * 表达式触发器工厂Bean */ @Bean(name = "mySecondExerciseJobTrigger") public CronTriggerFactoryBean mySecondExerciseJobTrigger(@Qualifier("mySecondExerciseJobBean") MethodInvokingJobDetailFactoryBean mySecondExerciseJobDetailFactoryBean) { CronTriggerFactoryBean tigger = new CronTriggerFactoryBean(); tigger.setJobDetail(mySecondExerciseJobDetailFactoryBean.getObject()); tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式 tigger.setName("general-mySecondExerciseJobTrigger"); return tigger; } }
将任务和触发器注册到调度器:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz; import org.quartz.Trigger; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; @Configuration public class QuartzConfig { /** * 调度器工厂Bean */ @Bean(name = "schedulerFactory") public SchedulerFactoryBean schedulerFactory(@Qualifier("myFirstExerciseJobTrigger") Trigger myFirstExerciseJobTrigger, @Qualifier("mySecondExerciseJobTrigger") Trigger mySecondExerciseJobTrigger) { SchedulerFactoryBean bean = new SchedulerFactoryBean(); // 覆盖已存在的任务 bean.setOverwriteExistingJobs(true); // 延时启动定时任务,避免系统未彻底启动却开始执行定时任务的状况 bean.setStartupDelay(15); // 注册触发器 bean.setTriggers(myFirstExerciseJobTrigger, mySecondExerciseJobTrigger); return bean; } }
任务持久化须要用到数据库,而初始化数据库的SQL能够从下载的发布版的文件中找到,好比,我在官网的Download页下载了当前版本的Full Distribution:Quartz 2.2.3 .tar.gz
,解压后在quartz-2.2.3\docs\dbTables
能找到初始化脚本,因我用的是MySQL的Innodb引擎,因此我用此脚本tables_mysql_innodb.sql
。
若是使用的是MethodInvokingJobDetailFactoryBean
,持久化会有以下报错:Couldn't store job: Unable to serialize JobDataMap for insertion into database because the value of
,咱们切换使用JobDetailFactoryBean
。
quartz.properties的配置有所不一样,好比会使用JobStoreTX,而后会指定数据源的信息:
# 线程调度器实例名 org.quartz.scheduler.instanceName = quartzScheduler # 线程池的线程数,即最多3个任务同时跑 org.quartz.threadPool.threadCount = 3 # 如何存储任务和触发器等信息 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX # 驱动代理 org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 表前缀 org.quartz.jobStore.tablePrefix = qrtz_ # 数据源 org.quartz.jobStore.dataSource = quartzDataSource # 是否集群 org.quartz.jobStore.isClustered = false # 数据源 # 驱动 org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver # 链接URL org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8&useSSL=true&&serverTimezone=Asia/Shanghai # 用户名 org.quartz.dataSource.quartzDataSource.user = root # 密码 org.quartz.dataSource.quartzDataSource.password = 123456 # 最大链接数 org.quartz.dataSource.quartzDataSource.maxConnections = 5
定义的任务有所不一样:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz.job; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.stereotype.Component; @Component @EnableScheduling public class MyFirstExerciseJob implements Job { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void execute(JobExecutionContext jobExecutionContext) { this.myJobBusinessMethod(); } public void myJobBusinessMethod() { this.logger.info("哇被触发了哈哈哈哈哈"); } }
注册任务和触发器的方式有所不一样:
/** * 方法调用任务明细工厂Bean */ @Bean(name = "myFirstExerciseJobBean") public JobDetailFactoryBean myFirstExerciseJobBean() { JobDetailFactoryBean jobDetail = new JobDetailFactoryBean(); jobDetail.setName("general-myFirstExerciseJob"); // 任务的名字 jobDetail.setGroup("general"); // 任务的分组 jobDetail.setJobClass(MyFirstExerciseJob.class); jobDetail.setDurability(true); return jobDetail; } /** * 表达式触发器工厂Bean */ @Bean(name = "myFirstExerciseJobTrigger") public CronTriggerFactoryBean myFirstExerciseJobTrigger(@Qualifier("myFirstExerciseJobBean") JobDetailFactoryBean myFirstExerciseJobBean) { CronTriggerFactoryBean tigger = new CronTriggerFactoryBean(); tigger.setJobDetail(myFirstExerciseJobBean.getObject()); tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式 tigger.setName("general-myFirstExerciseJobTrigger"); return tigger; }
注册全部任务和触发器:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz; import org.quartz.Trigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import javax.sql.DataSource; import java.io.IOException; import java.util.Properties; @Configuration public class QuartzConfig { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired @Qualifier(value = "primaryDataSource") private DataSource primaryDataSource; /** * 调度器工厂Bean */ @Bean(name = "schedulerFactory") public SchedulerFactoryBean schedulerFactory( Trigger... triggers) { SchedulerFactoryBean bean = new SchedulerFactoryBean(); Properties p = new Properties(); try { p.load(this.getClass().getClassLoader().getResourceAsStream("quartz.properties")); } catch (IOException e) { this.logger.error("加载quartz.properties失败", e); throw new Error(e); } bean.setQuartzProperties(p); // 覆盖已存在的任务 bean.setOverwriteExistingJobs(true); // 延时启动定时任务,避免系统未彻底启动却开始执行定时任务的状况 bean.setStartupDelay(15); // 注册触发器 bean.setTriggers(triggers); return bean; } }
有时候持久化的数据源用的是应用的数据源,可使用bean.setDataSource(dataSource)
设置或覆盖数据源。
好比,应用的数据源是这样的。
POM.XML:
<!-- 引入数据库链接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> <!-- Spring Boot JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
数据源配置:
spring.dataSource.primaryDataSource.type = com.alibaba.druid.pool.DruidDataSource spring.dataSource.primaryDataSource.url = jdbc:mysql://localhost:33306/quartz?characterEncoding=utf-8&useSSL=true&&serverTimezone=Asia/Shanghai&allowMultiQueries=true&autoReconnect=true spring.dataSource.primaryDataSource.username = root spring.dataSource.primaryDataSource.password = 123456 spring.dataSource.primaryDataSource.driverClassName = com.mysql.jdbc.Driver spring.dataSource.primaryDataSource.initialSize = 5 spring.dataSource.primaryDataSource.minIdle = 5 spring.dataSource.primaryDataSource.maxActive = 15 spring.dataSource.primaryDataSource.maxWait = 60000 spring.dataSource.primaryDataSource.timeBetweenEvictionRunsMillis = 60000 spring.dataSource.primaryDataSource.minEvictableIdleTimeMillis = 300000 spring.dataSource.primaryDataSource.validationQuery = SELECT 1 FROM DUAL spring.dataSource.primaryDataSource.testWhileIdle = true spring.dataSource.primaryDataSource.testOnBorrow = true spring.dataSource.primaryDataSource.testOnReturn = true
配置载入类:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.datasourcepool; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "spring.dataSource.primaryDataSource") public class DruidPrimaryDataSourceConfigProperties { private String type; private String url; private String username; private String password; private String driverClassName; private Integer initialSize; private Integer minIdle; private Integer maxActive; private Integer maxWait; private Integer timeBetweenEvictionRunsMillis; private Integer minEvictableIdleTimeMillis; private String validationQuery; private Boolean testWhileIdle; private Boolean testOnBorrow; private Boolean testOnReturn; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public Integer getInitialSize() { return initialSize; } public void setInitialSize(Integer initialSize) { this.initialSize = initialSize; } public Integer getMinIdle() { return minIdle; } public void setMinIdle(Integer minIdle) { this.minIdle = minIdle; } public Integer getMaxActive() { return maxActive; } public void setMaxActive(Integer maxActive) { this.maxActive = maxActive; } public Integer getMaxWait() { return maxWait; } public void setMaxWait(Integer maxWait) { this.maxWait = maxWait; } public Integer getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public Integer getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public Boolean getTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(Boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public Boolean getTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(Boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public Boolean getTestOnReturn() { return testOnReturn; } public void setTestOnReturn(Boolean testOnReturn) { this.testOnReturn = testOnReturn; } }
初始化数据源:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.datasourcepool; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class DruidPrimaryDataSourceConfig { @Autowired private DruidPrimaryDataSourceConfigProperties druidPrimaryDataSourceConfigProperties; @Bean public DataSource primaryDataSource (){ DruidDataSource datasource = new DruidDataSource(); /* 基础配置 */ datasource.setUrl(this.druidPrimaryDataSourceConfigProperties.getUrl()); datasource.setUsername(this.druidPrimaryDataSourceConfigProperties.getUsername()); datasource.setPassword(this.druidPrimaryDataSourceConfigProperties.getPassword()); datasource.setDriverClassName(this.druidPrimaryDataSourceConfigProperties.getDriverClassName()); /* 其余配置 */ datasource.setInitialSize(this.druidPrimaryDataSourceConfigProperties.getInitialSize()); datasource.setMinIdle(this.druidPrimaryDataSourceConfigProperties.getMinIdle()); datasource.setMaxActive(this.druidPrimaryDataSourceConfigProperties.getMaxActive()); datasource.setMaxWait(this.druidPrimaryDataSourceConfigProperties.getMaxWait()); datasource.setTimeBetweenEvictionRunsMillis(this.druidPrimaryDataSourceConfigProperties.getTimeBetweenEvictionRunsMillis()); datasource.setMinEvictableIdleTimeMillis(this.druidPrimaryDataSourceConfigProperties.getMinEvictableIdleTimeMillis()); datasource.setValidationQuery(this.druidPrimaryDataSourceConfigProperties.getValidationQuery()); datasource.setTestWhileIdle(this.druidPrimaryDataSourceConfigProperties.getTestWhileIdle()); datasource.setTestOnBorrow(this.druidPrimaryDataSourceConfigProperties.getTestOnBorrow()); datasource.setTestOnReturn(this.druidPrimaryDataSourceConfigProperties.getTestOnReturn()); return datasource; } }
可使用bean.setDataSource(dataSource)
设置或覆盖数据源:
package com.nicchagil.exercise.quartz.springbootquartzexercise.component.quartz; import org.quartz.Trigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import javax.sql.DataSource; import java.io.IOException; import java.util.Properties; @Configuration public class QuartzConfig { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired @Qualifier(value = "primaryDataSource") private DataSource primaryDataSource; /** * 调度器工厂Bean */ @Bean(name = "schedulerFactory") public SchedulerFactoryBean schedulerFactory( Trigger... triggers) { SchedulerFactoryBean bean = new SchedulerFactoryBean(); Properties p = new Properties(); try { p.load(this.getClass().getClassLoader().getResourceAsStream("quartz.properties")); } catch (IOException e) { this.logger.error("加载quartz.properties失败", e); throw new Error(e); } bean.setQuartzProperties(p); /* 使用应用的数据源 */ bean.setDataSource(this.primaryDataSource); // 覆盖已存在的任务 bean.setOverwriteExistingJobs(true); // 延时启动定时任务,避免系统未彻底启动却开始执行定时任务的状况 bean.setStartupDelay(15); // 注册触发器 bean.setTriggers(triggers); return bean; } }
在任务类上注解@DisallowConcurrentExecution
,好比此任务需耗时7秒,却配置5秒执行一次,注解后将会7秒才运行一次:
@Component @EnableScheduling @DisallowConcurrentExecution // 保证上一次任务执行完毕再执行下一任务 public class MyFirstExerciseJob implements Job {
日志:
2018-02-21 12:09:04.479 INFO 9496 --- [actory_Worker-3] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 5 2018-02-21 12:09:11.629 INFO 9496 --- [actory_Worker-2] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 6 2018-02-21 12:09:18.796 INFO 9496 --- [actory_Worker-1] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 7 2018-02-21 12:09:26.016 INFO 9496 --- [actory_Worker-3] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 8 2018-02-21 12:09:33.268 INFO 9496 --- [actory_Worker-2] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 9 2018-02-21 12:09:40.518 INFO 9496 --- [actory_Worker-1] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 10 2018-02-21 12:09:47.668 INFO 9496 --- [actory_Worker-3] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 11 2018-02-21 12:09:54.869 INFO 9496 --- [actory_Worker-2] c.n.e.q.s.c.q.job.MyFirstExerciseJob : 哇被触发了哈哈哈哈哈 x 12
下面配置展现了Quartz的必要配置:
RAMJobStore
# 线程调度器实例名 org.quartz.scheduler.instanceName = QuartzScheduler # 线程池的线程数,即最多3个任务同时跑 org.quartz.threadPool.threadCount = 3 # 实例ID org.quartz.scheduler.instanceId = AUTO # 如何存储任务和触发器等信息 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX # 驱动代理 org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate # 表前缀 org.quartz.jobStore.tablePrefix = qrtz_ # 是否集群 org.quartz.jobStore.isClustered = true
配置后,启动多个节点,你会发现: