写点什么

SpringBoot3 集成 Quartz

作者:知了一笑
  • 2023-08-13
    浙江
  • 本文字数:4467 字

    阅读完需:约 15 分钟

SpringBoot3集成Quartz

标签: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: true    properties:      org:        quartz:          # 调度器配置          scheduler:            instanceName: bootQuartzScheduler            instanceId: AUTO          # 存储配置          jobStore:            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate            tablePrefix: qrtz_            isClustered: true            misfireThreshold: 12000            clusterCheckinInterval: 15000            useProperties: false          # 线程池配置          threadPool:            threadNamePrefix: Boot_Job_Pool            threadPriority: 5            threadCount: 10            class: org.quartz.simpl.SimpleThreadPool
复制代码

三、Quartz 用法

对于任务管理的相关 Web 接口,采用 Swagger 文档组件,接口和实体类添加注解后,访问IP:Port/swagger-ui/index.html地址即可;


1、初始化加载

在服务启动时执行init初始化方法,查询quartz_job表中运行和暂停状态的任务,判断触发器是否存在,如果不存在则创建,如果存在则更新;


@Servicepublic class QuartzJobService {
@Resource private QuartzJobMapper quartzJobMapper ; @Resource private QuartzManage quartzManage;
@PostConstruct public 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、新增任务

在创建任务时,需要定义JobKeyTriggerKey的构建规则,Key 需要具备唯一性,通常使用任务表的主键 ID,任务一般是基于 Cron 表达式被调度执行的;


@Componentpublic class QuartzManage {
@Resource private 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 表达式即调度规则的更新,或者任务的执行参数更新;


@Componentpublic class QuartzManage {
@Resource private Scheduler scheduler ;
public void updateJob(QuartzJob quartzJob) { try { // 查询触发器Key TriggerKey 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,判断任务是非运行状态,则停止任务;


@Componentpublic class QuartzManage {
@Resource private 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,恢复任务正常执行;


@Componentpublic class QuartzManage {
@Resource private 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,然后立即执行一次任务;


@Componentpublic class QuartzManage {
@Resource private 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,彻底删除任务;


@Componentpublic class QuartzManage {
@Resource private 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 {    @Override    protected 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
复制代码


发布于: 刚刚阅读数: 2
用户头像

知了一笑

关注

公众号:知了一笑 2020-04-08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
SpringBoot3集成Quartz_Java_知了一笑_InfoQ写作社区