SpringBoot 中如何实现定时任务 ?
- 标签:Spring Boot、后端
🏛 业务架构师
Spring Boot 实现定时任务三个层次,按业务重要性选型:
1. @Scheduled(轻量单机):
java
@Component
public class ReportJob {
@Scheduled(cron = "0 0 2 * * ?")
public void dailyReport() { ... }
}启动类加 @EnableScheduling。局限必须说透:默认单线程串行、多实例重复执行、无失败告警与重试、改时间要发版——只能用于允许重复且幂等的内部场景。
2. 多实例必配分布式锁:Redis(Redisson)/DB 锁/ShedLock,保证同一时刻只有一个节点执行——@Scheduled 仍可保留,补齐多实例语义。
3. 调度平台(生产默认):XXL-Job / PowerJob,我的选型默认值:
- 控制台可视化管理:改 cron 不发版、手动触发、执行日志;
- 失败告警与重试——"账单任务失败无人知"是真实资损事故,告警能力比调度本身更值钱;
- 分片广播:大表处理按分片并行,跑批窗口从小时缩到分钟。
业务治理三条纪律:所有任务必须幂等(重跑安全);任务要有 owner 和告警接收人(资产化管理);跑批任务错峰(别全挤在整点压垮数据库)。
一句话:@Scheduled 会用即可,生产的答案调度平台——任务是企业资产,需要管理面而不只是触发器。
🏗 系统架构师
单机版:@EnableScheduling 加 @Scheduled;注意默认单线程调度器,多任务串行——配 spring.task.scheduling.pool.size 与线程命名。
分布式问题的演进选择(我按规模分层):
- 一到三个实例的小场景:ShedLock(DB/Redis 分布式锁保证单实例执行),侵入最小;
- 中大型:xxl-job 调度平台——调度与执行分离、手动触发、失败重试与告警、分片广播(大任务切片并行)、控制台审计,国内主流;
- K8s 原生:CronJob 跑「起容器、执行、退出」的批任务,适合离线计算,不占常驻资源。
架构红线:
- 任务幂等是前提——重试与补跑必然发生;
- 监控三件套:上次执行时间、成功率、耗时;哑火的任务是最隐蔽的生产事故,我用「超过预期时间未执行」的主动告警兜底,而不是等业务方报障;
- 耗时任务与在线流量隔离(独立部署或对 DB 限流),防止定时任务吃满共享资源。
💻 全栈开发专家
单机版 @Scheduled 三分钟上手:
java
@EnableScheduling
@SpringBootApplication
public class App { ... }
@Component
public class SyncTask {
@Scheduled(cron = "0 30 1 * * ?") // cron:秒 分 时 日 月 周
public void dailySync() { ... }
@Scheduled(fixedDelay = 5000) // 上次【结束】5s 后再跑
public void poll() { ... }
}两个必踩坑:默认单线程调度器(spring.task.scheduling.pool.size 调大或任务异步化);多实例重复跑。
生产标准形态是分布式调度。轻量方案 Redisson 锁:
java
@Scheduled(cron = "0 */10 * * * ?")
public void reconcile() {
RLock lock = redisson.getLock("job:reconcile");
if (!lock.tryLock()) return; // 其他实例在跑
try { reconcileService.run(); }
finally { lock.unlock(); }
}正式方案 xxl-job:调度中心统一管理(控制台配 cron、分片广播、失败重试、手动补跑)、执行器自动注册——对账、批量推送这类关键任务我必上 xxl-job,因为"跑没跑、跑成没跑成"可见可追。K8s CronJob 也可选(零依赖但无续跑/分片能力)。
选型口诀:单实例轻任务 @Scheduled + 锁,多实例关键任务 xxl-job。