一、简介
Quartz是一个完全由java编写的开源作业调度框架,适用用多种环境:单节点以及集群环境。集群环境下具有伸缩性、高可用性、负载均衡等特性。本章将主要介绍在集群环境下分布式部署的实现。
二、pom引入
基于springboot项目
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
三、配置
job的存储依赖于mysql
# 链接的mysql配置quartz会将job信息等存储到自己生成的表中
spring.datasource.url=jdbc:mysql://localhost:3306/database_name?characterEncoding=utf8&serverTimezone=UTC
# quzrtz configuration
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema = always
spring.quartz.overwrite-existing-jobs=true
spring.quartz.properties.org.quartz.scheduler.instanceName = MyClusteredScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount = 50
spring.quartz.properties.org.quartz.threadPool.threadPriority = 5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true
# Configure the jobStore
spring.quartz.properties.org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.useProperties=false
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=20000
四、实现
quartz框架主要核心组件包括调度器(scheduler)、触发器(Trigger)、作业(Job)。
- 调度器:作为作业的总指挥,做为定时任务容器,是quartz最上层的东西,它提携了所有触发器和作业,使它们协调工作,每个Scheduler都存有JobDetail和Trigger的注册,一个Scheduler中可以注册多个JobDetail和多个Trigger。
- 触发器:作为作业的操作者,Trigger做为作业的定时管理工具,一个Trigger只能对应一个作业实例,而一个作业实例可对应多个触发器。
- 作业为应用的功能模块,负责处理应用的业务逻辑。
4.1.JobManager
负责启动调度并绑定job和Tigger进行绑定,并执行任务的增删改查。
@Slf4j
@Component
public class TaskJobManager {
@Resource
private Scheduler schedulerQuartz;
// job可以按照类别放入不同的组已形成一定的隔离
private final static String TRIGGER_GROUP = "组名";
public void addCronJob(String jobName, JobDataModel jobData, String cron) {
try{
// 启动调度器
if (!schedulerQuartz.isStarted()) {
schedulerQuartz.start();
}
deleteJob(jobName);
addJob(jobName, jobData, cron);
} catch (SchedulerException e) {
log.error("addCronJob失败", e);
throw new GException(ErrorMessage.QUARTZ_SCHEDULER_EXCEPTION, "addCronJob失败");
}
}
private void addJob(String jobName, JobDataModel jobData, String cron) {
try {
log.info("构建新的Job, JobData:{}, Cron:{}", JSON.toJSONString(jobData), cron);
//构建job信息,不通的触发去可以与不通的job绑定
JobDetail jobDetail = JobBuilder.newJob(Job1.class)
.withIdentity(jobName, TRIGGER_GROUP)
.storeDurably()//即使没有Trigger关联时,也不需要删除该JobDetail
.build();
jobDetail.getJobDataMap().put(Cons.JOB_DATA, jobData);
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)//关联上述的JobDetail
.withIdentity(jobName, TRIGGER_GROUP)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))// "0/1 * * * * ?"
.build();
schedulerQuartz.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
log.error("创建定时任务失败", e);
throw new Exception("创建定时任务失败");
}
}
public void updateJob(String jobName, JobDataModel jobDataModel, String cron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger = (CronTrigger) schedulerQuartz.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//参数修改
JobKey jobKey = JobKey.jobKey(jobName, TRIGGER_GROUP);
JobDataModel jobDataMap = (JobDataModel) schedulerQuartz.getJobDetail(jobKey).getJobDataMap().get(Cons.JOB_DATA);
jobDataMap.setEtlTaskSch(jobDataModel.getEtlTaskSch());
jobDataMap.setCallFailRetryTimes(jobDataMap.getCallFailRetryTimes());
// 按新的trigger重新设置job执行
schedulerQuartz.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
log.error("更新定时任务失败" + e);
throw new Exception("更新定时任务失败");
}
}
public void deleteJob(String jobName) {
try {
val triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP);
schedulerQuartz.pauseTrigger(triggerKey);
if (schedulerQuartz.checkExists(triggerKey)) {
schedulerQuartz.unscheduleJob(TriggerKey.triggerKey(jobName, TRIGGER_GROUP));
}
val jobKey = JobKey.jobKey(jobName, TRIGGER_GROUP);
if (schedulerQuartz.checkExists(jobKey)) {
schedulerQuartz.deleteJob(jobKey);
}
} catch (SchedulerException e) {
log.error("更新定时任务失败" + e);
throw new Exception("删除定时任务失败");
}
}
}
4.2. Job
在调度器调起始时job会被触发器触发从而执行具体的job业务逻辑。
public class Job1 QuartzJobBean {
@Override
protected void executeInternal(@NonNull JobExecutionContext jobExecutionContext) {
// 获取job中的jobDataMap--构建job中某些数据可以存储到jobDataDetail中
val jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
val jobDataModel = (JobDataModel) jobDataMap.get(Cons.JOB_DATA);
try {
// 在此执行具体的业务
} catch (OtherServiceException e) {
log.error("任务执行异常, errorMessage:{}", e.getMsg());
}
}