分布式任务调度:从 Cron 到 XXL-Job#
“每天凌晨 3 点跑报表”——单机时一个 Cron 搞定。上集群后问题来了:3 个节点都执行,报表算 3 遍。分布式任务调度解决的就是:任务在集群里"只执行一次、可分片、可重试、可管理"。
一、单机 Cron 的局限#
二、分布式调度的核心机制#
1. 调度器与执行器分离#
2. 执行策略(防重复)#
3. 任务分片(大任务的解法)#
三、XXL-Job 实践#
XXL-Job 是国内最流行的开源方案(国人开发,文档全)。
集成(Go)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 任务执行器:注册任务
func init() {
exec := xxl.NewExecutor(
xxl.ServerAddr("http://scheduler:8080/xxl-job-admin"),
xxl.RegistryKey("delivery-job"),
)
exec.RegTask("report_daily", ReportDaily) // 注册任务
exec.Run()
}
// 任务方法(返回状态)
func ReportDaily(c *xxl.RunReq) string {
// 分片处理:c.Index / c.Total
processShard(c.Index, c.Total)
return "success"
}
|
关键配置#
四、任务设计规范#
五、踩坑记录#
- 任务不幂等:重试跑两遍 → 数据翻倍 → 任务必须幂等(用幂等键/状态机)
- 分片不均匀:按 ID 取模,数据分布不均 → 用业务维度分片(按用户/按区域)
- 执行器内存泄漏:任务里开 goroutine 不回收 → 执行器 OOM → 任务控制并发 + 超时
- 调度中心单点:admin 只部署一台 → 挂了任务全停 → admin 高可用(主备)
- 任务依赖:报表任务依赖数据同步任务完成 → 用"任务编排"(前驱/后继)或自检(数据就绪再算)
六、选型对比#
| 方案 |
特点 |
适用 |
| XXL-Job |
功能全、文档全、中文 |
绝大多数团队 |
| Quartz |
老牌、Java 原生 |
Java 单机/简单集群 |
| Elastic-Job |
分片强(ZooKeeper) |
大规模分片任务 |
| 自研(Redis 锁+Cron) |
轻量 |
任务少、不想引框架 |
| 云服务 |
托管 |
不想运维 |
选型建议:任务 < 20 个且简单 → Redis 锁 + 本地 Cron 够用;任务多/要分片/要管理 → XXL-Job。
七、什么时候必须上调度平台#
分布式任务调度的核心认知:
- 调度与执行分离:调度中心管"何时",执行器管"如何"
- 三件事:防重复(路由策略)、可分片(大任务)、可恢复(重试转移)
- 任务本身要幂等:调度平台再稳,任务不幂等也白搭
- 运维三件套:日志、告警、超时熔断
定时任务是最容易"静默出错"的系统:没人盯着它,错了也是半夜错。有了调度平台 + 日志告警,任务才从"玄学"变成"可管理"。
微信公众号「福清而不淡」
后端架构 · AI 工程 · 云原生的一线实践,扫码关注,不错过更新。
本文已同步发布到公众号,欢迎留言交流。