Quartz定时任务调度器

371 阅读5分钟

Quartz框架

轻量级的任务调度框架,支持作业调度、持久化、任务持久化、并发控制、失败重试等。与spring自带定时任务最主要的区别是他支持定时年月日时分秒存储

核心组件

调度器:Scheduler 任务 :Job 触发器:Trigger,包括SimpleTrigger和CronTrigger

  1. Job(任务):是一个接口,有一个方法 void execute(JobExecutionContext context) ,可以通过实现该接口来定义需要执行的任务(具体的逻辑代码)。
  1. JobDetail:Quartz每次执行Job时,都重新创建一个Job实例,会接收一个Job实现类,以便运行的时候通过newInstance()的反射调用机制去实例化Job。JobDetail是用来描述Job实现类以及相关静态信息,比如任务在scheduler中的组名等信息。
  1. Trigger(触发器):描述触发Job执行的时间触发规则实现类SimpleTrigger和CronTrigger可以通过crom表达式定义出各种复杂的调度方案。
  1. Calendar:是一些日历特定时间的集合。一个Trigger可以和多个 calendar关联,比如每周一早上10:00执行任务,法定假日不执行,则可以通过calendar进行定点排除。
  1. Scheduler(调度器):代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。Scheduler可以将Trigger绑定到某一JobDetail上,这样当Trigger被触发时,对应的Job就会执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。

Scheduler 调度器 -> Trigger 触发器-> JobDetail 工作策略 ->Job 工作

Quartz框架的工作流程

Quartz框架的工作流程如下:

  • 首先,定义需要执行的任务(Job),并指定任务的执行规则(Trigger)。
  • 其次,将任务和触发器注册到调度器中。
  • 最后,调度器按照指定的条件和规则执行任务。

使用

  1. 导入依赖

     <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-quartz -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-quartz</artifactId>
     </dependency>
    
  2. 实现定时业务逻辑

    创建一个业务类,继承 job 接口,并实现 execute() 方法,方法中即可实现相应业务逻辑

     import org.quartz.Job;
     import org.quartz.JobExecutionContext;
     import org.quartz.JobExecutionException;
      
     public class AlphaJob implements Job {
      
         @Override
         public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
             
             // 业务逻辑代码略
      
         }
     }
    
  3. 编写触发器类

    定义任务的调度规则,实现Trigger接口。

     public class HelloScheduler {
         public static void main(String[] args) throws SchedulerException {
             //1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
             JobDetail jobDetail = JobBuilder
                     .newJob(HelloJob.class) //定义Job类为HelloJob类,真正的执行逻辑所在
                     .withIdentity("myJob", "group1") //定义name 和 group
                     .usingJobData("message","hello myJob1") //加入属性到jobDataMap
                     .usingJobData("FloatJobValue",8.88f) //加入属性到jobDataMap
                     .build();
      
             //2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
             SimpleTrigger trigger = TriggerBuilder.newTrigger()
                     .withIdentity("myTrigger", "group1")
                     .startNow()
                     .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
                     .build();
             //3.创建schedule实例
             StdSchedulerFactory factory = new StdSchedulerFactory();
             Scheduler scheduler = factory.getScheduler();
             scheduler.start(); //启动
             scheduler.scheduleJob(jobDetail,trigger); // jobDetail和trigger加入调度
         }
     }
    

案例:

需求:假设你正在开发一个在线学习平台,需要实现以下定时任务:

每天凌晨1点,统计当天新增用户数量,并发送邮件通知管理员。 每周一凌晨2点,计算上一周的学习活跃用户,将活跃用户信息保存到数据库。

  1. 实现job逻辑
 @Component
 public class ActiveUserStatsJob implements Job {
 ​
     @Override
     public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
         // 在这里编写计算活跃用户信息的逻辑
         System.out.println("计算学习活跃用户并保存到数据库...");
     }
 }
 ​
 @Component
 public class NewUserStatsJob implements Job {
 ​
     @Override
     public void execute(JobExecutionContext context) throws JobExecutionException {
         // 在这里编写统计新增用户的逻辑
         System.out.println("统计新增用户数量并发送邮件通知管理员...");
     }
 }
 ​
  1. 配置触发器和调度器
 /**
  * quartz配置类
  * 注意: 添加后运行程序即可quartz自动运行
  * @version 1.0.0
  * @date 2023/8/3
  */
 @Configuration
 public class QuartzConfig {
 ​
     @Bean
     public JobDetail newUserStatsJobDetail() {
         return newJob(NewUserStatsJob.class)
                 .withIdentity("newUserStatsJob")
                 .storeDurably()
                 .build();
     }
 ​
     @Bean
     public Trigger newUserStatsJobTrigger() {
         return newTrigger()
                 .forJob(newUserStatsJobDetail())
                 .withIdentity("newUserStatsJobTrigger")
 //                .withSchedule(cronSchedule("0 0 1 * * ?")) // 每天凌晨1点执行
                 .withSchedule(cronSchedule("0 0/3 * * * ?")) // 3分钟
                 .build();
     }
 ​
     @Bean
     public JobDetail activeUserStatsJobDetail() {
         return newJob(ActiveUserStatsJob.class)
                 .withIdentity("activeUserStatsJob")
                 .storeDurably()
                 .build();
     }
 ​
     @Bean
     public Trigger activeUserStatsJobTrigger() {
         return newTrigger()
                 .forJob(activeUserStatsJobDetail())
                 .withIdentity("activeUserStatsJobTrigger")
 //                .withSchedule(cronSchedule("0 0 2 * * MON")) // 每周一凌晨2点执行
                 .withSchedule(cronSchedule("0 0/2 * * * ?")) // 两分钟执行
                 .build();
     }
 }
 ​

常见时间(corn语法)

 "30 * * * * ?" 每半分钟触发任务
 "30 10 * * * ?" 每小时的1030秒触发任务
 "30 10 1 * * ?" 每天11030秒触发任务
 "30 10 1 20 * ?" 每月2011030秒触发任务
 "30 10 1 20 10 ? *" 每年102011030秒触发任务
 "30 10 1 20 10 ? 2011" 2011102011030秒触发任务
 "30 10 1 ? 10 * 2011" 201110月每天11030秒触发任务
 "30 10 1 ? 10 SUN 2011" 201110月每周日11030秒触发任务
 "15,30,45 * * * * ?"15秒,30秒,45秒时触发任务

相比spring自带的定时触发器的好处

spring自带的定时触发器,如springtask

Spring task 优点:无需整合spring,作业类中就可以调用业务service 缺点:单线程;不能做数据存储型的定时任务,不能定时到年月日时分秒 Quartz 优点:多线程;可以做数据存储型的定时任务,维护性高; 缺点:需要整合spring,不能直接调用业务层service;