任务调度管理——Quartz入门


在这里插入图片描述

📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 Quartz专栏 ,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 云原生RabbitMQSpring全家桶GIT 等仍在更新,欢迎指导
📙Zookeeper Redis kafka docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待


一、Quartz 介绍

1. Quartz 是什么

相信同学们在项目开发中经常遇到诸如定时任务这类要求,常见的比如

  • 每隔5分钟查询账户金额
  • 每天晚上八点自动触发垃圾清理脚本
  • 周一到周五自动生成并发送报表邮件

这个时候我们就需要利用任务调度框架来负责,比如我们今天要说的Quartz

Quartz是一个功能强大的分布式任务调度框架,被广泛应用于各种复杂的任务调度场景中。其具有高可靠性、高扩展性和灵活性等特点,可以帮助开发人员轻松实现任务的管理和调度。

2. 与@scheduled注解比较

说到调度,Spring自己也提供了@scheduled的注解来实现调度。 那与Quartz相比,两者有什么异同,该怎么选呢?

特点Quartz@Scheduled
任务调度支持灵活的任务调度功能,可以按照固定的时间间隔、指定时间点或者某种条件进行调度任务支持定时任务调度,可以按照固定的时间间隔或者指定时间点调度任务
分布式支持分布式任务调度,可以在多个节点上同时运行任务不支持分布式任务调度,只能在单个节点上运行任务
可扩展性支持插件机制,可以通过自定义插件来扩展功能不支持插件机制,无法进行功能扩展
监控和管理提供了一套完善的监控和管理工具,可以监控任务的执行情况,并提供了Web界面进行管理不提供监控和管理工具,需要通过其他方式进行监控和管理
容错性具备容错机制,当任务执行失败或者节点故障时,可以进行任务重试或者切换到其他可用节点上继续执行不具备容错机制,任务执行失败后无法进行重试或者切换
并发性支持并发执行任务,可以同时执行多个任务只能串行执行任务,无法同时执行多个任务

通俗来说,就是一个简单易用但功能少,一个复杂但功能强大。像一些要求比较复杂的场景,比如定时邮件任务,由集群里某一台服务器来执行就行了,不需要每台服务器都发一遍邮件,这时候@schedual 就捉襟见肘了,而使用 Quartz 其自带的分布式控制就能轻松实现

二、Quartz 的用法

1. Quartz 的结构设计

我们先来看下Quartz 的核心结构设计:
在这里插入图片描述

其中涉及到了三个部分:调度器触发器作业

  • 调度器(Scheduler)
    调度器是 Quartz 的核心组件,负责创建、启动和调度任务。它可以与数据库进行交互,通过查询和更新任务信息来实现任务的调度和管理。

  • 触发器(Trigger)
    触发器用于指定作业的调度规则,决定何时执行作业。Quartz 提供了多种类型的触发器,如 SimpleTrigger、CronTrigger 等。触发器可以设置触发时间、重复次数和间隔等属性,可以与作业一起关联,形成调度关系。

  • 作业(Job)
    作业是需要被调度的任务,实现了 Job 接口。作业可以是简单的 Java 类,也可以是通过实现 Job 接口的类。Quartz 可以调度和执行不同类型的作业,如无状态作业和有状态作业。

简单的说就是job里记录着”我们要做的事情“;Trigger里放着“做这些事的时间”;而“协调众多任务”就得靠Scheduler了,一个需要注意的细节就是,一个作业,可以被多个Trigger触发,就比如发工资(job)在每个月(trigger)都会被执行,但在年末(trigger)也可能会触发(年终奖)

2. Quartz 使用demo

在使用之前,我们先要进行引用Quartz框架,如果你本身是spring-boot 2 以上的项目,直接在pom中加如下引用即可

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

单独引用的话,可以使用这个

<dependency>
     <groupId>org.quartz-scheduler</groupId>
     <artifactId>quartz</artifactId>
      <version>2.3.2</version>
</dependency>

比如我现在想实现上面提到过的,在每周一到周五的早上八点发送邮件的功能。我们可以这么写,先把我们的job写出来

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class SendEmailJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 在这里编写发送邮件的逻辑
        System.out.println("发送邮件");
        // 这里可以使用邮件发送的库,例如JavaMail等
    }
}

然后开始写实现的Demo :

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.text.ParseException;
import java.util.Date;

public class SchedulerDemo {

    public static void main(String[] args) throws SchedulerException, ParseException {
        // 创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 创建任务
        JobDetail job = JobBuilder.newJob(SendEmailJob.class)
                .withIdentity("sendEmailJob", "group1")
                .build();

        // 创建触发器,设置触发时间为每周一到周五的早上八点
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("sendEmailTrigger", "group1")
                .startAt(DateBuilder.nextGivenSecondDate(null, 1))
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 ? * MON-FRI"))
                .build();

        // 将任务和触发器添加到调度器中
        scheduler.scheduleJob(job, trigger);

        // 启动调度器
        scheduler.start();
    }
}

当然上述是一个Demo,真正在项目中使用,像 调度器触发器 都是通过Bean注册进容器中的,如下:

    @Bean
    public CronTriggerFactoryBean myJobTrigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(myJobDetail());
        cronTriggerFactoryBean.setCronExpression("0 0 8 ? * MON-FRI"); // 每5秒执行一次
        return cronTriggerFactoryBean;
    }

    @Bean
    public JobDetailFactoryBean myJobDetail() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(SendEmailJob.class);
        return jobDetailFactoryBean;
    }

此时的调度器启用,则是放在启动类加上 @EnableScheduling 注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. CRON表达式

需要注意的是,Trigger 的触发条件是使用CRON表达式的,CRON表达式是一种用于指定定时任务执行时间的字符串格式。它由7或6个字段(年份字段可省略)组成,形如* * * * * * ,每个字段用空格分隔,依次表示分钟、小时、日期、月份、星期几和年份。

0   15  10    *      *     ?    2024
秒  分  时  日期  月份  星期  年份
语法用法
0-59可以指定具体的秒数
分钟0-59可以指定具体的分钟数,例如,5表示每小时的第5分钟执行一次。也可以使用通配符表示每分钟都执行。还可以使用逗号,分隔多个值,例如5,10,15表示第5、10和15分钟执行。还可以使用连字符-表示一个范围,例如10-30表示从第10分钟到第30分钟之间的每分钟都执行。还可以使用斜杠/表示间隔执行,例如/5表示每5分钟执行一次。
小时0-23类似于分钟域,可以指定具体的小时数,使用通配符*、逗号,、连字符-和斜杠/进行设置。
日期1-31可以指定具体的日期,使用通配符*、逗号,、连字符-和斜杠/进行设置。还可以使用特殊字符L表示每个月的最后一天,例如L表示每个月的最后一天执行。还可以在日期字段之前加上W表示最接近指定日期的工作日(周一至周五)执行任务,例如15W表示最接近15号的工作日执行任务。还可以使用?表示该字段不指定具体日期。
月份1-12 或 JAN-DEC类似于日期域,可以指定具体的月份,使用通配符*、逗号,、连字符-和斜杠/进行设置。
星期几0-7 或 SUN-SAT可以指定具体的星期几,0和7都表示周日,1-6分别表示周一至周六,使用通配符*、逗号,、连字符-和斜杠/进行设置。还可以使用特殊字符L表示每个月的最后一个工作日执行任务。还可以使用#和数字表示每个月的第几个星期几执行任务,例如3#1表示每个月的第一个星期二执行任务。还可以使用?表示该字段不指定具体星期几。
年份空或 1970-2099类似于其它域,可以指定具体的年份,使用通配符*、逗号,、连字符-和斜杠/进行设置。

上面的域有些是支持特殊字符的,用来实现更复杂的时间控制:

  • 星号 (*)
    表示匹配任意值。可以在秒、分钟、小时、日期、月份和星期中使用。例如,在分钟域上使用 * 表示每分钟都执行。

  • 问号 (?)
    在日期和星期中用于替代具体的值,表示不确定的值。例如,在星期域上使用"?"表示不考虑星期,可以与日期域一起使用。

  • 斜线 (/)
    用于指定范围内的间隔值。例如,在小时域上使用"*/2"表示每隔两小时执行一次。

  • 逗号 (,)
    用于指定多个值。例如,在小时域上使用"1,5,10"表示1点、5点和10点都会执行。

  • 减号 (-)
    用于指定一个范围内的连续值。例如,在日期域上使用"1-5"表示1号到5号都会执行。

  • L
    用于表示最后的值。可以在日期和星期域中使用。例如,在日期域上使用"L"表示最后一天。

  • W
    用于指定最近的工作日。可以在日期域中使用。例如,"15W"表示最近的工作日到15号。

  • #
    用于指定每月的第几个星期几。可以在星期域中使用。例如,在星期域上使用"3#1"表示每月的第一个星期二。

我们用一点例子来解释上面这些规则的用法:

表达式含义
0 0/2 * * * ?每2分钟 执行任务
0 0/30 9-17 * * ?朝九晚五工作时间内每半小时
0 15 10 L * ?每月最后一日的上午10:15执行
0 15 10 ? * 6#3每月的第三个星期五上午10:15执行
0 10,44 14 ? 3 WED每年三月的星期三的下午2:10和2:44执行
0 0 10,14 ? * 2-6#1每个月的第一个星期一至星期五的上午10点和下午2点执行

三、 总结

上面我们简单介绍了Quartz是什么以及其基本用法,对于初学者来说,会用就是迈开了第一步,后续我们会详细展开对它的学习,包括理解它的原理与高级用法。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战斧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值