ToB企服应用市场:ToB评测及商务社交产业平台

标题: SpringBoot3集成Quartz [打印本页]

作者: 农妇山泉一亩田    时间: 2023-8-13 11:36
标题: SpringBoot3集成Quartz
目录

标签:Quartz.Job.Scheduler;
一、简介

Quartz由Java编写的功能丰富的开源作业调度框架,可以集成到几乎任何Java应用程序中,并且能够创建多个作业调度;
在实际的业务中,有很多场景依赖定时任务,比如常见的:订单超时处理,数据报表统计分析,会员等周期性管理,业务识别和预警通知等;
二、工程搭建

1、工程结构


2、依赖管理

在starter-quartz组件中,实际依赖的是quartz组件2.3.2版本,使用Quartz框架时,需要自定义任务和执行逻辑,以更加灵活的方式管理业务调度;
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-quartz</artifactId>
  4.     <version>${spring-boot.version}</version>
  5. </dependency>
复制代码
3、数据库

Quartz框架使用的表结构在如图的路径下,本文选择MySQL数据库存储,除此之外自定义两张表:quartz_job任务表和quartz_log任务执行日志表;

4、配置文件

在配置文件中使用Druid组件连接boot-quartz数据库,对于Quartz框架,主要配置数据库存储,调度器的基础信息,以及执行任务的线程池;
  1. spring:
  2.   # 定时器配置
  3.   quartz:
  4.     # 使用数据库存储
  5.     job-store-type: jdbc
  6.     # 初始化完成后自动启动调度程序
  7.     autoStartup: true
  8.     properties:
  9.       org:
  10.         quartz:
  11.           # 调度器配置
  12.           scheduler:
  13.             instanceName: bootQuartzScheduler
  14.             instanceId: AUTO
  15.           # 存储配置
  16.           jobStore:
  17.             class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
  18.             driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
  19.             tablePrefix: qrtz_
  20.             isClustered: true
  21.             misfireThreshold: 12000
  22.             clusterCheckinInterval: 15000
  23.             useProperties: false
  24.           # 线程池配置
  25.           threadPool:
  26.             threadNamePrefix: Boot_Job_Pool
  27.             threadPriority: 5
  28.             threadCount: 10
  29.             class: org.quartz.simpl.SimpleThreadPool
复制代码
三、Quartz用法

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

1、初始化加载

在服务启动时执行init初始化方法,查询quartz_job表中运行和暂停状态的任务,判断触发器是否存在,如果不存在则创建,如果存在则更新;
  1. @Service
  2. public class QuartzJobService {
  3.     @Resource
  4.     private QuartzJobMapper quartzJobMapper ;
  5.     @Resource
  6.     private QuartzManage quartzManage;
  7.     @PostConstruct
  8.     public void init () {
  9.         LambdaQueryWrapper<QuartzJob> queryWrapper = new LambdaQueryWrapper<>() ;
  10.         queryWrapper.in(QuartzJob::getState,JobState.JOB_RUN.getStatus(),JobState.JOB_STOP.getStatus());
  11.         List<QuartzJob> jobList = quartzJobMapper.selectList(queryWrapper);
  12.         jobList.forEach(quartzJob -> {
  13.             CronTrigger cronTrigger = quartzManage.getCronTrigger(quartzJob.getId()) ;
  14.             if (Objects.isNull(cronTrigger)){
  15.                 quartzManage.createJob(quartzJob);
  16.             } else {
  17.                 quartzManage.updateJob(quartzJob);
  18.             }
  19.         });
  20.     }
  21. }
复制代码
2、新增任务

在创建任务时,需要定义JobKey和TriggerKey的构建规则,Key需要具备唯一性,通常使用任务表的主键ID,任务一般是基于Cron表达式被调度执行的;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.     public void createJob (QuartzJob quartzJob){
  6.         try {
  7.             // 构建任务
  8.             JobDetail jobDetail = JobBuilder.newJob(QuartzRecord.class).withIdentity(getJobKey(quartzJob.getId())).build() ;
  9.             // 构建Cron调度器
  10.             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
  11.                                                 .cronSchedule(quartzJob.getCronExpres())
  12.                                                 .withMisfireHandlingInstructionDoNothing() ;
  13.             // 任务触发器
  14.             CronTrigger trigger = TriggerBuilder.newTrigger()
  15.                                                 .withIdentity(getTriggerKey(quartzJob.getId()))
  16.                                                 .withSchedule(scheduleBuilder).build() ;
  17.             jobDetail.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY,quartzJob);
  18.             scheduler.scheduleJob(jobDetail,trigger) ;
  19.             // 状态校验
  20.             checkStop(quartzJob) ;
  21.         } catch (SchedulerException e){
  22.             throw new RuntimeException("createJob Fail",e) ;
  23.         }
  24.     }
  25. }
复制代码
3、更新任务

先通过任务ID查询TriggerKey,对于更新来说,最常见的就是Cron表达式即调度规则的更新,或者任务的执行参数更新;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.     public void updateJob(QuartzJob quartzJob) {
  6.         try {
  7.             // 查询触发器Key
  8.             TriggerKey triggerKey = getTriggerKey(quartzJob.getId());
  9.             // 构建Cron调度器
  10.             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
  11.                                                 .cronSchedule(quartzJob.getCronExpres())
  12.                                                 .withMisfireHandlingInstructionDoNothing();
  13.             // 任务触发器
  14.             CronTrigger trigger = getCronTrigger(quartzJob.getId())
  15.                                 .getTriggerBuilder().withIdentity(triggerKey)
  16.                                 .withSchedule(scheduleBuilder).build();
  17.             trigger.getJobDataMap().put(QuartzJob.JOB_PARAM_KEY, quartzJob);
  18.             scheduler.rescheduleJob(triggerKey, trigger);
  19.             // 状态校验
  20.             checkStop(quartzJob) ;
  21.         } catch (SchedulerException e) {
  22.             throw new RuntimeException("updateJob Fail",e) ;
  23.         }
  24.     }
  25. }
复制代码
4、暂停任务

先通过任务ID查询JobKey,判断任务是非运行状态,则停止任务;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.     public void checkStop (QuartzJob quartzJob){
  6.         try {
  7.             if(quartzJob.getState() != JobState.JOB_RUN.getStatus()){
  8.                 this.scheduler.pauseJob(getJobKey(quartzJob.getId()));
  9.             }
  10.         } catch (SchedulerException e){
  11.             throw new RuntimeException("pauseJob Fail",e) ;
  12.         }
  13.     }
  14. }
复制代码
5、恢复任务

先通过任务ID查询JobKey,恢复任务正常执行;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.     public void resumeJob (Integer jobId){
  6.         try {
  7.             this.scheduler.resumeJob(getJobKey(jobId));
  8.         } catch (SchedulerException e){
  9.             throw new RuntimeException("resumeJob Fail",e) ;
  10.         }
  11.     }
  12. }
复制代码
6、执行一次

传入任务主体,再通过任务ID查询JobKey,然后立即执行一次任务;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.    
  6.     public void run (QuartzJob quartzJob){
  7.         try {
  8.             JobDataMap dataMap = new JobDataMap() ;
  9.             dataMap.put(QuartzJob.JOB_PARAM_KEY,quartzJob);
  10.             this.scheduler.triggerJob(getJobKey(quartzJob.getId()),dataMap);
  11.         } catch (SchedulerException e){
  12.             throw new RuntimeException("run Fail",e) ;
  13.         }
  14.     }
  15. }
复制代码
7、删除任务

先通过任务ID查询JobKey,彻底删除任务;
  1. @Component
  2. public class QuartzManage {
  3.     @Resource
  4.     private Scheduler scheduler ;
  5.     public void deleteJob (Integer jobId){
  6.         try {
  7.             scheduler.deleteJob(getJobKey(jobId));
  8.         } catch (SchedulerException e){
  9.             throw new RuntimeException("deleteJob Fail",e) ;
  10.         }
  11.     }
  12. }
复制代码
8、任务执行

Quartz被集成在Spring框架之后,任务类自然会以Bean对象的方式被管理,在任务创建时,设置要执行的作业类QuartzRecord,该类继承QuartzJobBean抽象类,通过重写executeInternal方法,来管理任务实际执行的逻辑;
  1. public class QuartzRecord extends QuartzJobBean {
  2.     @Override
  3.     protected void executeInternal(JobExecutionContext context) {
  4.         QuartzJob quartzJob = (QuartzJob)context.getMergedJobDataMap().get(QuartzJob.JOB_PARAM_KEY) ;
  5.         QuartzLogService quartzLogService = (QuartzLogService)SpringContextUtil.getBean("quartzLogService") ;
  6.         // 定时器日志记录
  7.         QuartzLog quartzLog = new QuartzLog () ;
  8.         quartzLog.setJobId(quartzJob.getId());
  9.         quartzLog.setBeanName(quartzJob.getBeanName());
  10.         quartzLog.setParams(quartzJob.getParams());
  11.         quartzLog.setCreateTime(new Date());
  12.         long beginTime = System.currentTimeMillis() ;
  13.         try {
  14.             // 加载并执行
  15.             Object target = SpringContextUtil.getBean(quartzJob.getBeanName());
  16.             Method method = target.getClass().getDeclaredMethod("run", String.class);
  17.             method.invoke(target, quartzJob.getParams());
  18.             long executeTime = System.currentTimeMillis() - beginTime;
  19.             quartzLog.setTimes((int)executeTime);
  20.             quartzLog.setState(LogState.LOG_SUS.getStatus());
  21.         } catch (Exception e){
  22.             // 异常信息
  23.             long executeTime = System.currentTimeMillis() - beginTime;
  24.             quartzLog.setTimes((int)executeTime);
  25.             quartzLog.setState(LogState.LOG_FAIL.getStatus());
  26.             quartzLog.setError(e.getMessage());
  27.         } finally {
  28.             // 保存执行日志
  29.             quartzLogService.insert(quartzLog) ;
  30.         }
  31.     }
  32. }
复制代码
四、参考源码
  1. 文档仓库:
  2. https://gitee.com/cicadasmile/butte-java-note
  3. 源码仓库:
  4. https://gitee.com/cicadasmile/butte-spring-parent
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4