Skip to content

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 与线程命名。

分布式问题的演进选择(我按规模分层):

  1. 一到三个实例的小场景:ShedLock(DB/Redis 分布式锁保证单实例执行),侵入最小;
  2. 中大型:xxl-job 调度平台——调度与执行分离、手动触发、失败重试与告警、分片广播(大任务切片并行)、控制台审计,国内主流;
  3. 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。