背景
quartz 可用于管理调度定时任务,有集群模式和单机模式,quartz 的单机模式部署,所有任务执行信息都在内存中保存,存在单点故障,quartz 的集群模式具备高可用,自动负载均衡等特点,可保障定时任务的执行。
1 SpringBoot + Mysql + Quartz 集群模式搭建
注: 集群模式依赖实例所在机器之间的时间同步,请自行部署 ntp 服务进行时间同步。
1.1 Quartz 相关表建立
- 去官网下载 quartz, [下载地址]
- 解压后,执行 docs/dbTables/tables_mysql_innodb.sql 脚本建表
- 检查 db 中是否存在以下 11 个表
+--------------------------+
| QRTZ_BLOB_TRIGGERS |
| QRTZ_CALENDARS |
| QRTZ_CRON_TRIGGERS |
| QRTZ_FIRED_TRIGGERS |
| QRTZ_JOB_DETAILS |
| QRTZ_LOCKS |
| QRTZ_PAUSED_TRIGGER_GRPS |
| QRTZ_SCHEDULER_STATE |
| QRTZ_SIMPLE_TRIGGERS |
| QRTZ_SIMPROP_TRIGGERS |
| QRTZ_TRIGGERS |
+--------------------------+
1.2 maven 中引入 Quartz 相关包
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
1.3 创建quartz配置文件
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.tablePrefix = QRTZ_
# 开启集群模式
org.quartz.jobStore.isClustered = true
# 集群实例检测时间间隔 ms
org.quartz.jobStore.clusterCheckinInterval = 5000
# misfire 任务的超时阈值 ms
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.txIsolationLevelReadCommitted = true
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.scheduler.instanceId= AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 工作线程的线程池设置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
特别解释一下这个参数 org.quartz.jobStore.misfireThreshold = 60000
, misfire 任务为错过调度触发时间的任务,而 misfireThreshold 为判定触发任务为 misfire 的判定条件,比如规定 11:30 要执行一次 Job, 如果因为实例挂掉或者线程池忙导致 11:33 才触发调度,超时了 3 分钟,超时时间 > 60000ms, 因此判定为 misfire。
判定为 misfire 的处理规则在后面的原理介绍相关文章会提及。
1.4 生成 ScheduleFactory Bean
@Configuration
public class SchedulerConfig {
@Autowired
private DataSource dataSource;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setSchedulerName("Cluster_Scheduler");
factory.setDataSource(dataSource);
factory.setApplicationContextSchedulerContextKey("applicationContext");
factory.setQuartzProperties(quartzProperties());
return factory;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
}
1.5 定义并添加 Job
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {
private static final Logger logger = LoggerFactory.getLogger(QuartzJob.class);
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
String taskName = context.getJobDetail().getJobDataMap().getString("name");
logger.info("---> Quartz job {}, {} <----", new Date(), taskName);
}
}
private void addJob(String id, String jobName) throws SchedulerException {
Scheduler scheduler = gSchedulerFactory.getScheduler();
JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
.withIdentity(id, JOB_GROUP_NAME)
.usingJobData("name", jobName).build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(String.valueOf(id), TRIGGER_GROUP_NAME)
.withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
scheduler.scheduleJob(jobDetail, trigger);
}
1.6 启动 Scheduler
public void start() throws SchedulerException {
gSchedulerFactory.getScheduler().start();
}
1.7 启动程序
quartz 集群和其他分布式集群不一样,集群实例之间不需要互相通信,只需要和DB 交互,通过 DB 感知其他势力,实现 Job 调度。因此只需要按照普通 java 程序启动即可,扩容也只需要新启动实例,不需要做额外配置。
下一篇会介绍 quartz 集群模式的原理。