Quartz 是一款开源且丰富特性的**“任务调度库”,能够集成与任何的java** 应用,在中小型公司有着广泛的应用,因此还是很有必要深入的了解一下的。大概会分三个小结来深入探讨一下Quartz在企业中的实践,前置知识建议还是去官网简单看下(http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/,需要说明的是,Quartz使用的是2.3.x, Springboot的版本是2.2.1.
在本节中,先看下基于MySQL的Quartz的配置与使用。
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
配置文件在官网有很详尽的说明了,官网链接:http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration/ 有些参数是后期优化的基础
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://your db
username: your acc
password: your pwd
application:
name: springboot-quartz-simple
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: NEVER
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
# batchTriggerAcquisitionMaxCount: 10
# batchTriggerAcquisitionFireAheadTimeWindow: 1000
# skipUpdateCheck: true
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 5000
dontSetAutoCommitFalse: true
acquireTriggersWithinLock: true
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 12
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
Quartz的配置类主要对调度器(Scheduler)做一些全局的配置,需要说明的是,有些配置是有默认值的,具体可以在SchedulerFactoryBean中查看,MySpringBeanJobFactory 这个类需要自己定义。
@Configuration
public class QuartzConfiguration {
private final QuartzProperties quartzProperties;
private final DataSource dataSource;
private final ApplicationContext applicationContext;
@Autowired
public QuartzConfiguration(DataSource dataSource, ApplicationContext applicationContext, QuartzProperties quartzProperties) {
this.dataSource = dataSource;
this.applicationContext = applicationContext;
this.quartzProperties = quartzProperties;
}
@Bean
public AutowiringSpringBeanJobFactory springBeanJobFactory() {
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory ();
autowiringSpringBeanJobFactory.setApplicationContext(applicationContext);
return autowiringSpringBeanJobFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(Trigger... triggers) {
SchedulerFactoryBean schedulerFactoryBean= new SchedulerFactoryBean();
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
schedulerFactoryBean.setDataSource(dataSource);
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
schedulerFactoryBean.setGlobalTriggerListeners(new MyTriggerListener());--job状态监听
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());
schedulerFactoryBean.setQuartzProperties(properties);
if(Objects.nonNull(triggers) && triggers.length > 0 ){
schedulerFactoryBean.setTriggers(triggers);
}
return schedulerFactoryBean;
}
}
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory autowireCapableBeanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
//monitor class
package com.atly.configuration;
import org.quartz.*;
import org.quartz.plugins.history.LoggingTriggerHistoryPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import static java.text.MessageFormat.*;
public class MyTriggerListener extends LoggingTriggerHistoryPlugin {
private static final Logger log = LoggerFactory.getLogger(MyTriggerListener.class);
//add execute time and next fire time when completed
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
if (!getLog().isInfoEnabled()) {
return;
}
String triggerCompleteMessage = "Trigger {0}.{1} completed firing job {2}.{3} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {5}, next fire time is {6, date, HH:mm:ss MM/dd/yyyy} , execution time is {7} ms ";
String instrCode = "UNKNOWN";
if (triggerInstructionCode == Trigger.CompletedExecutionInstruction.DELETE_TRIGGER) {
instrCode = "DELETE TRIGGER";
} else if (triggerInstructionCode == Trigger.CompletedExecutionInstruction.NOOP) {
instrCode = "DO NOTHING";
} else if (triggerInstructionCode == Trigger.CompletedExecutionInstruction.RE_EXECUTE_JOB) {
instrCode = "RE-EXECUTE JOB";
} else if (triggerInstructionCode == Trigger.CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) {
instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE";
} else if (triggerInstructionCode == Trigger.CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) {
instrCode = "SET THIS TRIGGER COMPLETE";
}
TriggerKey key = trigger.getKey();
JobKey jobKey = context.getJobDetail().getKey();
Object[] args = {
key.getGroup(),key.getName(),
jobKey.getGroup(),jobKey.getName(),
new Date(),instrCode,
trigger.getNextFireTime(),
context.getJobRunTime()
};
log.info(format(triggerCompleteMessage, args));
}
}
项目在本地跑起来后,在浏览器输入localhost:8080/,100个Job就创建了。可以随意的调试了。
@Component
@DisallowConcurrentExecution
public class QuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(QuartzJob.class);
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail = jobExecutionContext.getJobDetail();
log.info("job is completed job name:{}",jobDetail.getKey());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@GetMapping("/")
public void create( ) throws SchedulerException {
for (int i = 1; i <= 100; i++) {
JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
.withIdentity(UUID.randomUUID().toString().substring(1,10)+"-"+i,"jobKey.getGroup()")
.withDescription("default job description")
.storeDurably()
.build();
Trigger trigger =TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity(UUID.randomUUID().toString().substring(1,10)+i, "triggerKey.getGroup()")
.withDescription("default trigger description")
.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?"))
.startNow().build();
scheduler.scheduleJob(jobDetail,trigger);
log.info("create job: {}",i);
}
上面的跑起来的Job便于我们调试源码,Quartz的源码不多,但是一些基本的逻辑还是需要好好掌握以下。下节再次基础上进行源码调试。我们定义的QuartzJob类在我们的的代码里是没有调用的逻辑的,是哪个类的哪个方法调用的它呢?在调试后就会发现是JobRunShell::run第101行