标签:Quartz.Job.Scheduler;
一、简介
Quartz由Java编写的功能丰富的开源作业调度框架,可以集成到几乎任何Java应用程序中,并且能够创建多个作业调度;
在实际的业务中,有很多场景依赖定时任务,比如常见的:订单超时处理,数据报表统计分析,会员等周期性管理,业务识别和预警通知等;
二、工程搭建
1、工程结构
2、依赖管理
在starter-quartz
组件中,实际依赖的是quartz
组件2.3.2
版本,使用Quartz框架时,需要自定义任务和执行逻辑,以更加灵活的方式管理业务调度;
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId><version>${spring-boot.version}</version>
</dependency>
3、数据库
Quartz框架使用的表结构在如图的路径下,本文选择MySQL数据库存储,除此之外自定义两张表:quartz_job
任务表和quartz_log
任务执行日志表;
4、配置文件
在配置文件中使用Druid组件连接boot-quartz
数据库,对于Quartz框架,主要配置数据库存储,调度器的基础信息,以及执行任务的线程池;
spring:# 定时器配置quartz:# 使用数据库存储job-store-type: jdbc# 初始化完成后自动启动调度程序autoStartup: trueproperties:org:quartz:# 调度器配置scheduler:instanceName: bootQuartzSchedulerinstanceId: AUTO# 存储配置jobStore:class: org.springframework.scheduling.quartz.LocalDataSourceJobStoredriverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegatetablePrefix: qrtz_isClustered: truemisfireThreshold: 12000clusterCheckinInterval: 15000useProperties: false# 线程池配置threadPool:threadNamePrefix: Boot_Job_PoolthreadPriority: 5threadCount: 10class: org.quartz.simpl.SimpleThreadPool
三、Quartz用法
对于任务管理的相关Web接口,采用Swagger文档组件,接口和实体类添加注解后,访问IP:Port/swagger-ui/index.html
地址即可;
1、初始化加载
在服务启动时执行init
初始化方法,查询quartz_job
表中运行和暂停状态的任务,判断触发器是否存在,如果不存在则创建,如果存在则更新;
@Service
public class QuartzJobService {@Resourceprivate QuartzJobMapper quartzJobMapper ;@Resourceprivate QuartzManage quartzManage;@PostConstructpublic void init () {LambdaQueryWrapper<QuartzJob> queryWrapper = new LambdaQueryWrapper<>() ;queryWrapper.in(QuartzJob::getState,JobState.JOB_RUN.getStatus(),JobState.JOB_STOP.getStatus());List<QuartzJob> jobList = quartzJobMapper.selectList(queryWrapper);jobList.forEach(quartzJob -> {CronTrigger cronTrigger = quartzManage.getCronTrigger(quartzJob.getId()) ;if (Objects.isNull(cronTrigger)){quartzManage.createJob(quartzJob);} else {quartzManage.updateJob(quartzJob);}});}
}
2、新增任务
在创建任务时,需要定义JobKey
和TriggerKey
的构建规则,Key需要具备唯一性,通常使用任务表的主键ID,任务一般是基于Cron表达式被调度执行的;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void createJob (QuartzJob quartzJob){try {// 构建任务JobDetail jobDetail = JobBuilder.newJob(QuartzRecord.class).withIdentity(getJobKey(quartzJob.getId())).build() ;// 构建Cron调度器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpres()).withMisfireHandlingInstructionDoNothing() ;// 任务触发器CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(quartzJob.getId())).withSchedule(scheduleBuilder).build() ;jobDetail.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY,quartzJob);scheduler.scheduleJob(jobDetail,trigger) ;// 状态校验checkStop(quartzJob) ;} catch (SchedulerException e){throw new RuntimeException("createJob Fail",e) ;}}
}
3、更新任务
先通过任务ID查询TriggerKey
,对于更新来说,最常见的就是Cron表达式即调度规则的更新,或者任务的执行参数更新;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void updateJob(QuartzJob quartzJob) {try {// 查询触发器KeyTriggerKey triggerKey = getTriggerKey(quartzJob.getId());// 构建Cron调度器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpres()).withMisfireHandlingInstructionDoNothing();// 任务触发器CronTrigger trigger = getCronTrigger(quartzJob.getId()).getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();trigger.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY, quartzJob);scheduler.rescheduleJob(triggerKey, trigger);// 状态校验checkStop(quartzJob) ;} catch (SchedulerException e) {throw new RuntimeException("updateJob Fail",e) ;}}
}
4、暂停任务
先通过任务ID查询JobKey
,判断任务是非运行状态,则停止任务;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void checkStop (QuartzJob quartzJob){try {if(quartzJob.getState() != JobState.JOB_RUN.getStatus()){this.scheduler.pauseJob(getJobKey(quartzJob.getId()));}} catch (SchedulerException e){throw new RuntimeException("pauseJob Fail",e) ;}}}
5、恢复任务
先通过任务ID查询JobKey
,恢复任务正常执行;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void resumeJob (Integer jobId){try {this.scheduler.resumeJob(getJobKey(jobId));} catch (SchedulerException e){throw new RuntimeException("resumeJob Fail",e) ;}}
}
6、执行一次
传入任务主体,再通过任务ID查询JobKey
,然后立即执行一次任务;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void run (QuartzJob quartzJob){try {JobDataMap dataMap = new JobDataMap() ;dataMap.put(QuartzJob.JOB_PARAM_KEY,quartzJob);this.scheduler.triggerJob(getJobKey(quartzJob.getId()),dataMap);} catch (SchedulerException e){throw new RuntimeException("run Fail",e) ;}}
}
7、删除任务
先通过任务ID查询JobKey
,彻底删除任务;
@Component
public class QuartzManage {@Resourceprivate Scheduler scheduler ;public void deleteJob (Integer jobId){try {scheduler.deleteJob(getJobKey(jobId));} catch (SchedulerException e){throw new RuntimeException("deleteJob Fail",e) ;}}
}
8、任务执行
Quartz被集成在Spring框架之后,任务类自然会以Bean对象的方式被管理,在任务创建时,设置要执行的作业类QuartzRecord
,该类继承QuartzJobBean
抽象类,通过重写executeInternal
方法,来管理任务实际执行的逻辑;
public class QuartzRecord extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) {QuartzJob quartzJob = (QuartzJob)context.getMergedJobDataMap().get(QuartzJob.JOB_PARAM_KEY) ;QuartzLogService quartzLogService = (QuartzLogService)SpringContextUtil.getBean("quartzLogService") ;// 定时器日志记录QuartzLog quartzLog = new QuartzLog () ;quartzLog.setJobId(quartzJob.getId());quartzLog.setBeanName(quartzJob.getBeanName());quartzLog.setParams(quartzJob.getParams());quartzLog.setCreateTime(new Date());long beginTime = System.currentTimeMillis() ;try {// 加载并执行Object target = SpringContextUtil.getBean(quartzJob.getBeanName());Method method = target.getClass().getDeclaredMethod("run", String.class);method.invoke(target, quartzJob.getParams());long executeTime = System.currentTimeMillis() - beginTime;quartzLog.setTimes((int)executeTime);quartzLog.setState(LogState.LOG_SUS.getStatus());} catch (Exception e){// 异常信息long executeTime = System.currentTimeMillis() - beginTime;quartzLog.setTimes((int)executeTime);quartzLog.setState(LogState.LOG_FAIL.getStatus());quartzLog.setError(e.getMessage());} finally {// 保存执行日志quartzLogService.insert(quartzLog) ;}}
}
四、参考源码
文档仓库:
https://gitee.com/cicadasmile/butte-java-note源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent