Spring Boot 4.0 定时任务:Quartz、XXL-JOB 与延时任务
前言
订单超时关闭、Outbox 重试和日报生成都需要后台执行。简单 @Scheduled 很容易开始,但多实例、错过执行和可观测性会逐渐成为问题。
这篇教程用 Quartz 扫描 Outbox,并说明 Quartz、XXL-JOB 和延时队列各自解决什么问题。
配套源码:dvsusan/spring-boot4-demo。
最终结果

图 1:触发器启动任务,任务读取 PENDING 消息并完成投递。
第 1 步:创建 Quartz Job
import org.springframework.beans.factory.annotation.Autowired;
// 同一个 Job 不允许在上一次执行结束前再次进入。
@DisallowConcurrentExecution
public class OutboxRelayJob extends QuartzJobBean {
private OutboxService outboxService;
// Quartz 创建实例后由 Spring 注入任务依赖。
@Autowired
public void setOutboxService(OutboxService outboxService) {
this.outboxService = outboxService;
}
@Override
protected void executeInternal(JobExecutionContext context) {
int pending = outboxService.pending().size();
log.info("outbox relay job: pendingCount={}", pending);
}
}@DisallowConcurrentExecution 阻止同一个 Quartz Job 并发执行。它不能替代分布式租约,多个调度集群节点仍要依赖 Quartz 持久化 JobStore。
第 2 步:注册任务和触发器
// 持久化 JobDetail,方便多个 Trigger 或管理端复用。
@Bean
public JobDetail outboxRelayJobDetail() {
return JobBuilder.newJob(OutboxRelayJob.class)
.withIdentity("outboxRelayJob")
.storeDurably()
.build();
}
@Bean
public Trigger outboxRelayTrigger(JobDetail jobDetail) {
return TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(30)
.repeatForever())
.build();
}项目默认使用 RAMJobStore。生产集群应配置 Quartz JDBC JobStore,让多个节点共享触发器和执行状态。
第 3 步:启动任务
mvn clean package -DskipTests
java -jar demo-web/target/demo-web-1.0.0-SNAPSHOT.jar \
--server.port=18096 \
--demo.chapter4.jobs.enabled=true日志会出现:
outbox relay job: pendingCount=0任务会在应用启动后运行,每 30 秒执行一次。
第 4 步:先写一条 Outbox 消息
curl -X POST "http://localhost:18096/api/v1/chapter4/outbox" \
--data-urlencode "eventId=evt-job-1" \
--data-urlencode "aggregateId=SO1001" \
--data-urlencode "payload={\"orderNo\":\"SO1001\"}"等待下一轮任务日志,pendingCount 至少为 1。
Quartz、XXL-JOB、延时任务怎么选?
| 方案 | 优点 | 代价 | 适用场景 |
|---|---|---|---|
@Scheduled | 最简单 | 多实例协调弱 | 单机、轻量任务 |
| Quartz | 触发器完整、可持久化 | 配置和运维成本较高 | 业务定时任务 |
| XXL-JOB | 调度中心、日志、失败重试 | 需要独立平台 | 企业多服务任务 |
| MQ 延时消息 | 适合事件驱动 | 受消息队列能力限制 | 订单超时、延迟通知 |
选择依据是任务是否需要集中管理、补跑、分片和运行记录,而不是只看当前代码量。
常见问题排查
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 任务完全不执行 | Trigger 未绑定 Job | 检查 Bean 和 Scheduler |
| 多实例重复执行 | RAMJobStore 各节点独立 | 使用 Quartz JDBC/XXL-JOB |
| 执行时间不断漂移 | 简单 @Scheduled 固定延迟 | 改用 Cron 或 Quartz |
| 任务并发堆积 | 业务耗时超过触发间隔 | 禁止并发、分片或队列化 |
练习题
把任务间隔改为 Cron,并增加失败次数与任务耗时日志。
总结
Quartz 提供任务模型和触发模型,真正的生产可靠性还依赖触发记录、执行日志、幂等、告警和集群协调。
下一篇把订单文档写入 Elasticsearch,建立独立搜索读模型。
上一节:Outbox 最终一致性
下一节:Elasticsearch 搜索
13 个企业级实战项目 · 源码 + 教程 + 答疑
简历包装指导AI Agent 实战高并发·微服务Offer 导向持续更新3000+ 同行交流1V1 答疑3 天包退
覆盖高并发、微服务、分布式与 AI Agent:100万QPS短链、企业智能知识库、商城微服务、SaaS点餐、秒杀、刷题小程序、代码生成器、AI Agent 等。券后 ¥129(原价 ¥159),不满意 3 天包退。
