上次本身搭建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
配置後,啓動多個節點,你會發現: