定时任务是日常开发中非常常见的功能。
对于简单的任务处理Spring的@Scheduled非常好用。
如果处理更复杂的情况,比如需要宕机恢复或者集群调度,那么Quartz是个不错的轻量级方案。
一些重量级的第三方任务调度系统也是基于Quartz扩展的,比如XXL-JOB
Quartz的模块
Quartz模块.png-
Trigger定义了何时触发任务,主要是两种SimpleTrigger和CronTigger,其他Tigger基本都可以通过这两种实现。Trigger还可以定义错过的任务如何处理。下表是说明:
trigger.jpeg -
Calendar与Trigger相反,Calendar定义哪些时间是特例,不能执行任务。Calendar的优先级高于Trigger。HolidayCalendar比较常用,定义了哪些节日是特殊情况。
calendar.jpeg -
Job 负责定义任务所处理的逻辑,实现类需要实现org.quartz.Job接口
public interface Job {
void execute(JobExecutionContext context) throws JobExecutionException;
}
- 通过抛出JobExecutionException可以强制控制任务后续处理
public class JobExecutionException extends SchedulerException {
private boolean refire = false;//true: 重新执行任务(不会触发下一次)
private boolean unscheduleTrigg = false;//true: 直接标记Trigger完成
private boolean unscheduleAllTriggs = false;//true: 直接标记所有和Job相关的Trigger都已经完成
}
- Stateful Job。不同于一般的无状态任务,可以同时执行。有状态任务不能同时执行,而且需要保存状态。根据需要给可以Job类添加@PersistJobDataAfterExecution 或 @DisallowConcurrentExecution
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public interface StatefulJob extends Job {
}
- JobDetail 保存Job的元信息,包括类定义和设置。
- SchedulerFactory负责初始化,读取配置文件,然后创建Scheduler
- Scheduler是中枢调度器,负责管理Trigger/JobDetail和3个调度线程
-
QuartzSchedulerThread 主调度线程
调度任务主流程 (1).png
-
- MisfireHandler 错失触发的任务恢复线程,。更新Trigger的触发时间。
- ClusterManager 集群协调线程。定期心跳,自动recover。同主程序中的recover。
SpringBoot2.0集成Quartz
从boot2.0开始,增加了对Quartz的自动装配,以前需要自己处理。
以集群配置为例,最基本的操作是这样的:
- Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 编写具体的job
public class TestJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("Test job executed.");
}
}
- 定义JobDetail, 把Job的class类型传入。Spring自动处理
@Bean
public JobDetail testJob(){
return JobBuilder
.newJob(TestJob.class)
.withIdentity("TestJob")
.storeDurably()
.requestRecovery()
.build();
}
- 定义Trigger, 通过Key关联JobDetail。Spring 自动处理。
@Bean
public Trigger testTrigger(){
return TriggerBuilder.newTrigger()
.withIdentity("TestTrigger")
.forJob("TestJob")
.withSchedule(CronScheduleBuilder
.cronSchedule("0/6 * * * * ? ")
.withMisfireHandlingInstructionDoNothing())
.build();
}
- 如果需要不同的数据库,定义一个@Primary主库,和一个@QuartzDataSource quartz专用库
@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@QuartzDataSource
@ConfigurationProperties(prefix="spring.datasource.quartz")
public DataSource quartzDataSource() {
return DataSourceBuilder.create().build();
}
- application.properties 都有默认配置,第一行启用数据库,后面两行是cluster功能
spring.quartz.job-store-type=jdbc
spring.quartz.org.quartz.scheduler.instanceId = AUTO
spring.quartz.org.quartz.jobStore.isClustered = true
- 虽然Spring提供了自动建库的功能,但是第一次建完之后需要改成never
spring.quartz.jdbc.initializeSchema=ALWAYS
#spring.quartz.jdbc.initializeSchema=NEVER
-
手动建库可以从官网下载的全家桶,或者Jar包中获得
- 全家桶:{dir}/docs/dbTables/tables_{database}.sql
- Jar:classpath:/org/quartz/impl/jdbcjobstore/tables_{database}.sql
-
想看源码的可以从org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration进
网友评论