:聊点啥?咱们先唠唠
哎呀,每次有人问我 quartz 插件怎么用,我都想笑。这玩意儿看着挺复杂,其实就像咱们小区门口那个老修车师傅,你把问题说明白,他手到擒来。今天咱不整那些虚头巴脑的概念,就实实在在地掰开揉碎了讲讲 quartz 任务调度,从最基础的怎么跑起来,到后面搞集群那些花活儿。放心,保证大白话,听我给你捋顺了。
第一步:先搞明白 Quartz 是个啥玩意儿
Quartz 是啥?简单说,就是个开源的任务调度框架。它能让你的 Java 应用在某个特定的时间点或者按一定频率去执行特定的任务。比如:
- 每天凌晨 3 点自动清理数据库垃圾
- 每 5 分钟检查一次用户设备是否在线
- 收到消息后 10 秒内触发一个复杂的业务流程
你看,这些事儿要么得准时准点,要么得重复执行,这时候 quartz 就派上用场了。它不关心你的业务逻辑,只负责“在指定时间做指定事”。这就像你家闹钟,你设定了时间,它就响,不管你起不来还是睡不着。
Quartz 有两种核心模式:
“简单模式”适合小项目,直接把调度器跑在同一个 JVM 里;“集群模式”适合生产环境,多个服务器协同工作,确保任务不会因为单点故障而丢失。
咱们今天先从简单模式入手,后面再讲集群。记住,先跑通单机,再考虑集群,这老话儿没错。
第二步:环境准备,磨刀不误砍柴工
想用 quartz,你得有个 Java 环境,这不用我多说了吧?除了 JDK,你还需要把 quartz 的依赖加到项目中。如果是 Maven 项目,在 pom.xml 里加这几行:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>3.3.2</version> < 建议用最新稳定版 -->
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>3.3.2</version>
</dependency>
这里用 3.3.2 版本只是举个例子,建议你查查最新版,通常官方都会推荐一个稳定版本。版本不一致可能导致一些兼容性问题,所以还是统一好。
接下来,创建一个 Quartz 的配置类,比如叫 SchedulerConfig:
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.jdbcjobstore.JobStoreCMT;
import org.quartz.impl.jdbcjobstore.JobStoreTX;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.JobBuilder.newJob;
public class SchedulerConfig {
public static Scheduler getScheduler() throws SchedulerException {
// 1. 创建调度器工厂
StdSchedulerFactory factory = new StdSchedulerFactory();
factory.setJobStoreClass(JobStoreTX.class); // 使用事务性 JobStore
// 2. 获取调度器实例
return factory.getScheduler();
}
// 示例:创建并启动一个简单的任务
public static void startSimpleJob() throws SchedulerException {
Scheduler scheduler = getScheduler();
// 定义任务类
JobDetail job = newJob(MyJob.class)
.withIdentity("myJob", "group1")
.build();
// 定义触发器:立即执行,然后每 10 秒执行一次
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
// 将任务和触发器注册到调度器
scheduler.scheduleJob(job, trigger);
// 启动调度器
scheduler.start();
}
}
第三步:写个任务,让 Quartz 跑起来
任务类(Job)是你要执行的代码逻辑。Quartz 规定,任务类必须实现 org.quartz.Job 接口,并且有一个无参构造函数。比如咱们创建一个叫 MyJob 的类:
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 这里写你的业务逻辑
System.out.println("任务执行了!当前时间:" + System.currentTimeMillis());
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
注意这个 execute 方法,它会被 quartz 调用。方法里可以干啥?写业务逻辑就行。比如发送邮件、更新缓存、调用第三方 API 等。关键是,不要在这里直接写数据库操作或长时间阻塞的代码,后面会讲为啥。
现在,你可以在 SchedulerConfig 里调用 startSimpleJob> 方法,看看效果。如果一切正常,你会看到控制台每 10 秒打印一次时间戳。
第四步:深入理解:调度器的核心概念
Quartz 有几个关键概念,搞懂了后面用起来才顺手:
“Job”是任务本身,是你要执行的代码逻辑;“Trigger”是触发器,决定了什么时候执行任务;“Scheduler”是调度器,负责管理 Job 和 Trigger 的关系。
还有一个容易被忽略的:JobDetail,它描述了 Job 的详细信息,比如名字、组别、要传递给 Job 的参数等。就像你要搬家,除了知道去哪儿,还得知道带啥家具,对吧?
举个例子,咱们修改一下 SchedulerConfig,创建一个带参数的任务:
public static void startParameterizedJob() throws SchedulerException {
Scheduler scheduler = getScheduler();
// 创建带参数的任务
JobDetail job = newJob(MyParameterizedJob.class)
.withIdentity("paramJob", "group1")
.usingJobData("param1", "Hello Quartz!")
.build();
// 创建触发器
Trigger trigger = newTrigger()
.withIdentity("paramTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(15)
.repeatForever())
.build();
// 注册任务和触发器
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
然后在 MyParameterizedJob 里接收参数:
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.JobDataMap;
public class MyParameterizedJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String param1 = dataMap.getString("param1");
System.out.println("执行任务:" + jobKey);
System.out.println("参数:" + param1);
}
}
这样,每次任务执行时都会打印出传递的参数“Hello Quartz!”。这很实用,比如你可以根据不同的场景传递不同的配置信息给任务。
第五步:集群配置实战:如何让任务不丢
单机模式够用吗?对于小项目或测试环境,够。但真上生产,你得考虑高可用。这时候就得用集群模式了。集群的核心思想是:多个服务器协同工作,互相备份。当一个服务器挂了,其他服务器能接手它的任务。
配置集群主要分几步:
- 使用共享数据库:所有服务器都连接同一个数据库来存储调度信息
- 配置不同的 JobStore:推荐使用
JobStoreCMT或JobStoreTX - 设置集群模式:在
quartz.properties文件里启用集群 - 启动多个调度器实例:每个服务器上都运行一个 Quartz 调度器
以 JobStoreTX 为例,修改 SchedulerConfig:
public class SchedulerConfig {
public static Scheduler getScheduler() throws SchedulerException {
StdSchedulerFactory factory = new StdSchedulerFactory();
// 配置数据库连接信息(需要提前创建 quartz 表)
Properties props = new Properties();
props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
props.put("org.quartz.jobStore.dataSource", "myDS");
props.put("org.quartz.jobStore.driverClassName", "com.mysql.cj.jdbc.Driver");
props.put("org.quartz.jobStore.url", "jdbc:mysql://localhost/quartz?useSSL=false");
props.put("org.quartz.jobStore.user", "root");
props.put("org.quartz.jobStore.password", "password");
props.put("org.quartz.jobStore.useProperties", "true");
// 启用集群模式
props.put("org.quartz.scheduler.instanceName", "QuartzScheduler1");
props.put("org.quartz.scheduler.instanceId", "AUTO");
props.put("org.quartz.jobStore.isClustered", "true");
factory.initialize(props);
return factory.getScheduler();
}
}
注意这个 org.quartz.jobStore.isClustered 属性,把它设为 true 就开启了集群模式。你需要确保所有服务器上的 Quartz 都连接到同一个数据库,并且数据库里有 quartz 的表结构。你可以用官方提供的 SQL 脚本来创建这些表:
https://github.com/quartz-scheduler/quartz-scheduler/releases/download/v3.3.2/quartz-3.3.2-jobStoreSQL.sql
第六步:常见陷阱与最佳实践
用 quartz 的时候,有几个坑得避开:
- 任务执行时间过长:如果任务需要执行很久(比如几分钟),可能会导致 quartz 调度器卡住。这时候可以考虑用
StatefulJob,或者把长任务拆分成多个小任务 - 数据库连接池耗尽:如果任务频繁访问数据库,可能会耗尽连接池。建议使用连接池并合理配置
- 任务丢失:在集群模式下,如果服务器重启,可能会丢失部分任务。使用共享数据库可以避免这个问题
- JobDetail 和 Trigger 的命名:建议使用有意义的命名,方便排查问题
最佳实践:
对于重要任务,使用
StatefulJob并设置requestRecovery属性为true,这样即使服务器重启也能恢复任务
举个例子:
TriggerBuilder.newTrigger()
.withIdentity("importantTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule().repeatForever())
.usingJobData("recovery", true)
.build();
这样,如果服务器重启,quartz 会自动重新执行这个任务。
:从入门到进阶的完整路线
好了,今天就聊到这儿。一下 Quartz 的使用路线:
- 先搞懂基本概念:Job、Trigger、Scheduler
- 配置单机模式,跑通最简单的任务
- 学习参数传递,让任务更灵活
- 理解 JobStore 的工作原理
- 进阶学习集群模式,提升系统可用性
- 掌握最佳实践,避免踩坑
Quartz 功能很强大,但配置起来确实有点复杂。关键在于:分步走,先简单后复杂。等你把单机模式玩明白了,再慢慢扩展到集群,这样不容易被搞懵。
最后送大家一句老话:“罗马不是一天建成的,Quartz 也不是一天能精通的”。多动手实践,多看看源码,慢慢就上手了。有什么问题,评论区见!