1 基本概念
以下基本概念均对应Quartz的API。
1.1 任务
JobKey
包括jobName和jobGroup。同一调度器中,任务的JobKey是唯一的,被Scheduler用来管理Job。
Job
代表定时任务要执行的内容,只包含一个execute方法,执行的内容写在方法里:
public interface Job {
void execute(JobExecutionContext context) throws JobExecutionException;
}
JobDetail
存储Job的基本属性,包括JobKey和JobClass(Job的实现类):
public interface JobDetail extends Serializable, Cloneable {
JobKey getKey();
String getDescription();
Class<? extends Job> getJobClass();
JobDataMap getJobDataMap();
boolean isDurable();
boolean isPersistJobDataAfterExecution();
boolean isConcurrentExectionDisallowed();
boolean requestsRecovery();
Object clone();
JobBuilder getJobBuilder();
}
重要属性:
JobClass:Job的实现类ConcurrentExectionDisallowed:表明任务不能有多个节点同时执行。PersistJobDataAfterExecution:告诉Quartz在成功执行execution方法后,更新JobDetail。Durability:Job的信息会被持久化。RequestsRecovery:如果Job在某个节点执行失败,在下次Scheduler重启时任务会被重新执行。
JobBuilder
构建任务的工具类。
1.2 Trigger
代表定时任务的触发规则。
Simple Trigger
适合简单的规则,如在具体的时间点执行一次。
Cron Trigger
适合较复杂的规则。通常使用Cron Trigger。
TriggerBuilder
构建Trigger的工具类。
1.3 监听器
监听器注册到Scheduler,监听Trigger、Job或者Scheduler的操作。
JobListener
TriggerListener
SchedulerListener
1.4 Scheduler
代表调度器,是Quartz的核心组件,可以增删Job、触发Trigger等。
public interface Scheduler {
String getSchedulerName() throws SchedulerException;
String getSchedulerInstanceId() throws SchedulerException;
SchedulerContext getContext() throws SchedulerException;
void start() throws SchedulerException;
void startDelayed(int var1) throws SchedulerException;
boolean isStarted() throws SchedulerException;
void standby() throws SchedulerException;
boolean isInStandbyMode() throws SchedulerException;
void shutdown() throws SchedulerException;
void shutdown(boolean var1) throws SchedulerException;
boolean isShutdown() throws SchedulerException;
SchedulerMetaData getMetaData() throws SchedulerException;
List<JobExecutionContext> getCurrentlyExecutingJobs() throws SchedulerException;
void setJobFactory(JobFactory var1) throws SchedulerException;
ListenerManager getListenerManager() throws SchedulerException;
Date scheduleJob(JobDetail var1, Trigger var2) throws SchedulerException;
Date scheduleJob(Trigger var1) throws SchedulerException;
void scheduleJobs(Map<JobDetail, Set<? extends Trigger>> var1, boolean var2) throws SchedulerException;
void scheduleJob(JobDetail var1, Set<? extends Trigger> var2, boolean var3) throws SchedulerException;
boolean unscheduleJob(TriggerKey var1) throws SchedulerException;
boolean unscheduleJobs(List<TriggerKey> var1) throws SchedulerException;
Date rescheduleJob(TriggerKey var1, Trigger var2) throws SchedulerException;
void addJob(JobDetail var1, boolean var2) throws SchedulerException;
void addJob(JobDetail var1, boolean var2, boolean var3) throws SchedulerException;
boolean deleteJob(JobKey var1) throws SchedulerException;
boolean deleteJobs(List<JobKey> var1) throws SchedulerException;
void triggerJob(JobKey var1) throws SchedulerException;
void triggerJob(JobKey var1, JobDataMap var2) throws SchedulerException;
void pauseJob(JobKey var1) throws SchedulerException;
void pauseJobs(GroupMatcher<JobKey> var1) throws SchedulerException;
void pauseTrigger(TriggerKey var1) throws SchedulerException;
void pauseTriggers(GroupMatcher<TriggerKey> var1) throws SchedulerException;
void resumeJob(JobKey var1) throws SchedulerException;
void resumeJobs(GroupMatcher<JobKey> var1) throws SchedulerException;
void resumeTrigger(TriggerKey var1) throws SchedulerException;
void resumeTriggers(GroupMatcher<TriggerKey> var1) throws SchedulerException;
void pauseAll() throws SchedulerException;
void resumeAll() throws SchedulerException;
List<String> getJobGroupNames() throws SchedulerException;
Set<JobKey> getJobKeys(GroupMatcher<JobKey> var1) throws SchedulerException;
List<? extends Trigger> getTriggersOfJob(JobKey var1) throws SchedulerException;
List<String> getTriggerGroupNames() throws SchedulerException;
Set<TriggerKey> getTriggerKeys(GroupMatcher<TriggerKey> var1) throws SchedulerException;
Set<String> getPausedTriggerGroups() throws SchedulerException;
JobDetail getJobDetail(JobKey var1) throws SchedulerException;
Trigger getTrigger(TriggerKey var1) throws SchedulerException;
TriggerState getTriggerState(TriggerKey var1) throws SchedulerException;
void addCalendar(String var1, Calendar var2, boolean var3, boolean var4) throws SchedulerException;
boolean deleteCalendar(String var1) throws SchedulerException;
Calendar getCalendar(String var1) throws SchedulerException;
List<String> getCalendarNames() throws SchedulerException;
boolean interrupt(JobKey var1) throws UnableToInterruptJobException;
boolean interrupt(String var1) throws UnableToInterruptJobException;
boolean checkExists(JobKey var1) throws SchedulerException;
boolean checkExists(TriggerKey var1) throws SchedulerException;
void clear() throws SchedulerException;
}
1.5 JobStore
持久化JobDetail、Trigger、Scheduler等数据的地方,可以是数据库(JDBCJobStore)、RAM(RAMJobStore)等。JobStore不需要使用者直接调用,只需要在使用时指定。
2 集成Spring Boot
2.1 集成
Spring Boot已经集成了Quartz(通过QuartzAutoConfiguration自动配置),要使用时只要在application.yml中加入Quartz的配置,配置以spring.quartz.properties开头:
spring:
quartz:
job-store-type: jdbc
properties:
org:
quartz:
scheduler:
# 调度器名称
instanceName: DefaultQuartzScheduler
# 如果使用集群,instanceId要唯一,必须设置成AUTO
instanceId:AUTO
threadPool:
# 线程池实现类
class: org.quartz.impl.SimpleThreadPool
threadCount: 10
threadPriority: 5
# 线程名称前缀
threadNamePrefix: quartz
threadInheritContextClassLoaderOfInitializingThread: true
jobStore:
misfireThreshold: 60000
# 持久化方法:数据库
class: org.quartz.impl.jdbcJobStore.JobStoreTX
dirverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 数据库表名前缀
tablePrefix: qrtz_
# 是否使用集群
isClustered: true
2.2 实现Job
实现Job或InterruptedJob接口。
2.3 Spring Boot管理Job
Spring Boot会自动管理JobDetail或Trigger类型的Bean,并根据JobStore持久化,以下为一个例子:
@Configuration
public class QuartzConfig {
@Bean
public JobDetail testJobDetail() {
return JobBuilder.newJob(TestJob.class)
.withIdentity("testJob", "group")
.storeDurably()
.build();
}
@Bean
public Trigger testJobTrigger() {
return TriggerBuilder.newTrigger()
.withIdentity("testJobTrigger", "group")
.startNow()
.withSchedule(simpleSchedule().withIntervalInSeconds(1).repeatForever())
.forJob(new JobKey("testJob", "group"))
.build();
}
}
2.4 覆盖已存在的job
如果更改了定时任务配置文件中的信息,如cron表达式等,新的表达式在重启后不能生效,原因是没有设置overrideExistingJobs参数。Spring Boot没有提供这个参数的配置,需要手动设置:
@Configuration
@AutoConfigureAfter(QuartzAutoConfiguration.class)
public class QuartzSupportConfig {
@Autowired(required = false)
private List<Trigger> triggers;
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@PostConstruct
public void quartzScheduler() throws SchedulerException {
schedulerFactoryBean.setOverrideExistingJobs(true);
if (triggers != null) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
for (Trigger trigger : triggers) {
scheduler.reschedulerJob(trigger.getKey(), trigger);
}
}
}
}
3 集群
使用集群要配置以下几点:
- Quartz通过数据库管理集群,数据表结构可从官网获得;
- 集群只能使用
JDBCJobStore,如果使用了和项目中不同的数据源,需要另外配置。
3.1 持久化
以下是Quartz的默认数据库表,JobDetail、Trigger、Scheduler等都会保存在对应名称的表中:
| Table Name | Description |
|---|---|
| QRTZ_CALENDARS | 存储Quartz的Calendar信息 |
| QRTZ_CRON_TRIGGERS | 存储CronTrigger,包括Cron表达式和时区信息 |
| QRTZ_FIRED_TRIGGERS | 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
| QRTZ_PAUSED_TRIGGER_GRPS | 存储已暂停的Trigger组的信息 |
| QRTZ_SCHEDULER_STATE | 存储少量的有关Scheduler的状态信息,和别的Scheduler实例 |
| QRTZ_LOCKS | 存储程序的悲观锁的信息 |
| QRTZ_JOB_DETAILS | 存储每一个已配置的Job的详细信息 |
| QRTZ_JOB_LISTENERS | 存储有关已配置的JobListener的信息 |
| QRTZ_SIMPLE_TRIGGERS | 存储简单的Trigger,包括重复次数、间隔、以及已触的次数 |
| QRTZ_BLOG_TRIGGERS | Trigger作为Blob类型存储 |
| QRTZ_TRIGGER_LISTENERS | 存储已配置的TriggerListener的信息 |
| QRTZ_TRIGGERS | 存储已配置的Trigger的信息 |
3.2 负载均衡
quartz_lock是Quartz的锁表,使用SELECT ... FOR UPDATE语句获取行锁,保证同一时刻只有一个节点会执行定时任务。锁分为5种,分别用于实现多个节点对Job、Trigger、Calendar等访问的同步控制:
CALENDAR_ACCESS
JOB_ACCESS
MISFIRE_ACCESS
STATE_ACCESS
TRIGGER_ACCESS
具体流程:

3.3 故障转移
RequestsRecovery设置为true时,任务可以实现故障转移。
以下摘自Configuration of JDBC-JobStoreCMT
Fail-over occurs when one of the nodes fails while in the midst of executing one or more jobs. When a node fails, the other nodes detect the condition and identify the jobs in the database that were in progress within the failed node. Any jobs marked for recovery (with the “requests recovery” property on the JobDetail) will be re-executed by the remaining nodes. Jobs not marked for recovery will simply be freed up for execution at the next time a related trigger fires.