分布式任务调度框架:XXL-Job / ElasticJob / DolphinScheduler / SchedulerX 架构对比与选型
定时任务从单机走向分布式,"高可用、分片、防重复执行"是绕不开的三大难题。本文用一个统一的三阶段模型,把 XXL-Job、ElasticJob、DolphinScheduler、SchedulerX 四大主流框架的架构、差异和选型一次性讲清楚。
一、为什么需要分布式任务调度
在单体应用里,我们用 Timer、ScheduledExecutorService 或 Quartz 就能完成定时任务。但一旦进入分布式集群,同一个应用部署了多台机器,问题就来了:
- 重复执行:每台机器上都有定时任务,到点后同一个任务被执行多次,导致数据重复、脏数据;
- 单点故障:只在一台机器上跑,这台机器挂了,任务就彻底停摆;
- 无法分片:一个任务要处理 100 万条数据,单机串行处理太慢,需要多台机器并行处理不同数据;
- 运维困难:任务的启停、重跑、监控、告警都没有统一入口。
分布式任务调度框架就是为了解决这些问题而生。不同框架选择了不同的架构路线,主要分歧集中在三个问题上:
- 中心化 还是 去中心化?
- 服务端推送 还是 客户端自主?
- 调度与执行解耦 还是 合一?
下面我们逐个拆解。
二、四大框架架构详解
2.1 XXL-Job:中心式调度,轻量易用
XXL-Job 于 2015 年开源,核心设计目标是"开发迅速、学习简单、轻量级、易扩展",是目前国内中小团队使用最广泛的调度框架之一。
它采用调度与执行分离的架构,分为两部分:
- 调度中心(admin):独立部署的 Web 服务,负责管理任务、按 cron 触发、路由、监控、告警;
- 执行器(executor):业务应用引入 SDK,负责真正执行任务。
#mermaid-svg-pzR22GYMyh7m0fKk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pzR22GYMyh7m0fKk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pzR22GYMyh7m0fKk .error-icon{fill:#552222;}#mermaid-svg-pzR22GYMyh7m0fKk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pzR22GYMyh7m0fKk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pzR22GYMyh7m0fKk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pzR22GYMyh7m0fKk .marker.cross{stroke:#333333;}#mermaid-svg-pzR22GYMyh7m0fKk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pzR22GYMyh7m0fKk p{margin:0;}#mermaid-svg-pzR22GYMyh7m0fKk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pzR22GYMyh7m0fKk .cluster-label text{fill:#333;}#mermaid-svg-pzR22GYMyh7m0fKk .cluster-label span{color:#333;}#mermaid-svg-pzR22GYMyh7m0fKk .cluster-label span p{background-color:transparent;}#mermaid-svg-pzR22GYMyh7m0fKk .label text,#mermaid-svg-pzR22GYMyh7m0fKk span{fill:#333;color:#333;}#mermaid-svg-pzR22GYMyh7m0fKk .node rect,#mermaid-svg-pzR22GYMyh7m0fKk .node circle,#mermaid-svg-pzR22GYMyh7m0fKk .node ellipse,#mermaid-svg-pzR22GYMyh7m0fKk .node polygon,#mermaid-svg-pzR22GYMyh7m0fKk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pzR22GYMyh7m0fKk .rough-node .label text,#mermaid-svg-pzR22GYMyh7m0fKk .node .label text,#mermaid-svg-pzR22GYMyh7m0fKk .image-shape .label,#mermaid-svg-pzR22GYMyh7m0fKk .icon-shape .label{text-anchor:middle;}#mermaid-svg-pzR22GYMyh7m0fKk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pzR22GYMyh7m0fKk .rough-node .label,#mermaid-svg-pzR22GYMyh7m0fKk .node .label,#mermaid-svg-pzR22GYMyh7m0fKk .image-shape .label,#mermaid-svg-pzR22GYMyh7m0fKk .icon-shape .label{text-align:center;}#mermaid-svg-pzR22GYMyh7m0fKk .node.clickable{cursor:pointer;}#mermaid-svg-pzR22GYMyh7m0fKk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pzR22GYMyh7m0fKk .arrowheadPath{fill:#333333;}#mermaid-svg-pzR22GYMyh7m0fKk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pzR22GYMyh7m0fKk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pzR22GYMyh7m0fKk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pzR22GYMyh7m0fKk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pzR22GYMyh7m0fKk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pzR22GYMyh7m0fKk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pzR22GYMyh7m0fKk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pzR22GYMyh7m0fKk .cluster text{fill:#333;}#mermaid-svg-pzR22GYMyh7m0fKk .cluster span{color:#333;}#mermaid-svg-pzR22GYMyh7m0fKk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pzR22GYMyh7m0fKk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pzR22GYMyh7m0fKk rect.text{fill:none;stroke-width:0;}#mermaid-svg-pzR22GYMyh7m0fKk .icon-shape,#mermaid-svg-pzR22GYMyh7m0fKk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pzR22GYMyh7m0fKk .icon-shape p,#mermaid-svg-pzR22GYMyh7m0fKk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pzR22GYMyh7m0fKk .icon-shape .label rect,#mermaid-svg-pzR22GYMyh7m0fKk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pzR22GYMyh7m0fKk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pzR22GYMyh7m0fKk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pzR22GYMyh7m0fKk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 执行器集群
调度中心集群
抢锁
抢锁
抢锁
调度中心-1
调度中心-2
调度中心-3
MySQL
schedule_lock 行锁
执行器-1
执行器-2
执行器-3
核心机制:数据库行锁保证单点触发
调度中心支持多节点部署做 HA,但调度前会执行:
sql
SELECT * FROM xxl_job_lock WHERE lock_name = 'schedule_lock' FOR UPDATE;
这是 MySQL 行锁(独占锁),同一时刻只有一个调度中心节点能抢到锁、执行任务调度,其他节点阻塞等待。抢到锁的节点触发任务后,通过路由策略(轮询、随机、故障转移、分片广播等)选择一个执行器,下发任务。
优点:
- 轻量级、开箱即用,一分钟上手;
- 功能丰富:Web 可视化管理、GLUE 脚本在线编辑、日志查看、失败告警;
- 调度中心与执行器解耦,执行器分布式 HA。
缺点:
- 调度是单点串行的(虽然 HA),加调度中心节点只提升可用性,不提升调度吞吐;
- 任务量大(万级、秒级)时,DB 锁竞争会带来明显调度延迟,数据库压力大;
- 数据分片能力较弱(分片广播需要业务自己配合)。
2.2 ElasticJob:无中心,数据分片为特色
ElasticJob 最初由当当网开源,现在是 Apache ShardingSphere 的子项目 。它的定位是"轻量级无中心化解决方案 ",最大的特色是数据分片。
注意:ElasticJob 没有独立的调度中心,它是一个 jar 包,直接嵌入业务应用。业务应用本身就是"调度 + 执行"一体的节点,ZooKeeper 只负责协调。
#mermaid-svg-eXgzmNwdug9x5kiU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eXgzmNwdug9x5kiU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eXgzmNwdug9x5kiU .error-icon{fill:#552222;}#mermaid-svg-eXgzmNwdug9x5kiU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eXgzmNwdug9x5kiU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eXgzmNwdug9x5kiU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eXgzmNwdug9x5kiU .marker.cross{stroke:#333333;}#mermaid-svg-eXgzmNwdug9x5kiU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eXgzmNwdug9x5kiU p{margin:0;}#mermaid-svg-eXgzmNwdug9x5kiU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eXgzmNwdug9x5kiU .cluster-label text{fill:#333;}#mermaid-svg-eXgzmNwdug9x5kiU .cluster-label span{color:#333;}#mermaid-svg-eXgzmNwdug9x5kiU .cluster-label span p{background-color:transparent;}#mermaid-svg-eXgzmNwdug9x5kiU .label text,#mermaid-svg-eXgzmNwdug9x5kiU span{fill:#333;color:#333;}#mermaid-svg-eXgzmNwdug9x5kiU .node rect,#mermaid-svg-eXgzmNwdug9x5kiU .node circle,#mermaid-svg-eXgzmNwdug9x5kiU .node ellipse,#mermaid-svg-eXgzmNwdug9x5kiU .node polygon,#mermaid-svg-eXgzmNwdug9x5kiU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eXgzmNwdug9x5kiU .rough-node .label text,#mermaid-svg-eXgzmNwdug9x5kiU .node .label text,#mermaid-svg-eXgzmNwdug9x5kiU .image-shape .label,#mermaid-svg-eXgzmNwdug9x5kiU .icon-shape .label{text-anchor:middle;}#mermaid-svg-eXgzmNwdug9x5kiU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eXgzmNwdug9x5kiU .rough-node .label,#mermaid-svg-eXgzmNwdug9x5kiU .node .label,#mermaid-svg-eXgzmNwdug9x5kiU .image-shape .label,#mermaid-svg-eXgzmNwdug9x5kiU .icon-shape .label{text-align:center;}#mermaid-svg-eXgzmNwdug9x5kiU .node.clickable{cursor:pointer;}#mermaid-svg-eXgzmNwdug9x5kiU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eXgzmNwdug9x5kiU .arrowheadPath{fill:#333333;}#mermaid-svg-eXgzmNwdug9x5kiU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eXgzmNwdug9x5kiU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eXgzmNwdug9x5kiU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eXgzmNwdug9x5kiU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eXgzmNwdug9x5kiU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eXgzmNwdug9x5kiU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eXgzmNwdug9x5kiU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eXgzmNwdug9x5kiU .cluster text{fill:#333;}#mermaid-svg-eXgzmNwdug9x5kiU .cluster span{color:#333;}#mermaid-svg-eXgzmNwdug9x5kiU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eXgzmNwdug9x5kiU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eXgzmNwdug9x5kiU rect.text{fill:none;stroke-width:0;}#mermaid-svg-eXgzmNwdug9x5kiU .icon-shape,#mermaid-svg-eXgzmNwdug9x5kiU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eXgzmNwdug9x5kiU .icon-shape p,#mermaid-svg-eXgzmNwdug9x5kiU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eXgzmNwdug9x5kiU .icon-shape .label rect,#mermaid-svg-eXgzmNwdug9x5kiU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eXgzmNwdug9x5kiU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eXgzmNwdug9x5kiU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eXgzmNwdug9x5kiU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 业务应用集群(内嵌 ElasticJob)
业务节点-1
分片项 0
业务节点-2
分片项 1
业务节点-3
分片项 2
ZooKeeper
注册 / 选主 / 分片协调
核心机制:数据分片(一个任务,多片并行)
ElasticJob 把一个任务 拆成 N 个分片项(sharding item,数字 0 到 N-1)。每个业务节点执行同一份代码,但通过 ShardingContext 拿到自己被分到的分片项,再用它过滤数据:
java
public class MyJob implements SimpleJob {
@Override
public void execute(ShardingContext context) {
int item = context.getShardingItem(); // 本节点的分片项:0/1/2
int total = context.getShardingTotalCount(); // 分片总数:3
// 只处理自己那一片:id % 3 == 分片项
List<Data> list = query("SELECT * FROM t WHERE id % ? = ?", total, item);
process(list);
}
}
"主"是干什么的?
ElasticJob 会用 ZK 选举出一个 Leader(业务节点之一),但它不是全局主:
- Leader 只负责在节点上下线、分片数变化时,做一次分片划分;
- 定时触发不依赖它,每个节点本地都有定时(Quartz);
- Leader 自己也执行任务,挂了就重新选举。
优点:
- 无中心,无单点,节点对等;
- 数据分片能力强,单个重任务可被多节点并行处理,节点增减时自动 re-sharding;
- 支持弹性扩容、失效转移。
缺点:
- 调度和执行都在业务进程内,对业务服务有资源和稳定性压力(重任务会拖慢同进程接口);
- 没有独立的可视化管理 UI,运维能力弱;
- 不支持工作流编排。
最佳实践:重任务不要和核心业务混部,把 ElasticJob 单独部署在专门的应用/机器上,既保留无中心分片,又实现进程隔离。
2.3 DolphinScheduler:去中心化多 Master,专注工作流
DolphinScheduler(简称 DS)是 Apache 顶级项目,定位是"分布式、易扩展的可视化 DAG 工作流编排平台",擅长复杂任务依赖和海量工作流调度。
它的核心服务包括:
- Master:负责调度------切分 DAG、判断依赖、任务分片、监控状态、下发任务;
- Worker:负责执行------本地跑脚本或远程调用,独立进程;
- API:提供 RESTful 接口;
- Alert:告警服务;
- 注册中心:支持 ZooKeeper、Etcd、JDBC 多种实现。
#mermaid-svg-MnTakInIWK0HzXQl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MnTakInIWK0HzXQl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MnTakInIWK0HzXQl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MnTakInIWK0HzXQl .error-icon{fill:#552222;}#mermaid-svg-MnTakInIWK0HzXQl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MnTakInIWK0HzXQl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MnTakInIWK0HzXQl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MnTakInIWK0HzXQl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MnTakInIWK0HzXQl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MnTakInIWK0HzXQl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MnTakInIWK0HzXQl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MnTakInIWK0HzXQl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MnTakInIWK0HzXQl .marker.cross{stroke:#333333;}#mermaid-svg-MnTakInIWK0HzXQl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MnTakInIWK0HzXQl p{margin:0;}#mermaid-svg-MnTakInIWK0HzXQl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MnTakInIWK0HzXQl .cluster-label text{fill:#333;}#mermaid-svg-MnTakInIWK0HzXQl .cluster-label span{color:#333;}#mermaid-svg-MnTakInIWK0HzXQl .cluster-label span p{background-color:transparent;}#mermaid-svg-MnTakInIWK0HzXQl .label text,#mermaid-svg-MnTakInIWK0HzXQl span{fill:#333;color:#333;}#mermaid-svg-MnTakInIWK0HzXQl .node rect,#mermaid-svg-MnTakInIWK0HzXQl .node circle,#mermaid-svg-MnTakInIWK0HzXQl .node ellipse,#mermaid-svg-MnTakInIWK0HzXQl .node polygon,#mermaid-svg-MnTakInIWK0HzXQl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MnTakInIWK0HzXQl .rough-node .label text,#mermaid-svg-MnTakInIWK0HzXQl .node .label text,#mermaid-svg-MnTakInIWK0HzXQl .image-shape .label,#mermaid-svg-MnTakInIWK0HzXQl .icon-shape .label{text-anchor:middle;}#mermaid-svg-MnTakInIWK0HzXQl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MnTakInIWK0HzXQl .rough-node .label,#mermaid-svg-MnTakInIWK0HzXQl .node .label,#mermaid-svg-MnTakInIWK0HzXQl .image-shape .label,#mermaid-svg-MnTakInIWK0HzXQl .icon-shape .label{text-align:center;}#mermaid-svg-MnTakInIWK0HzXQl .node.clickable{cursor:pointer;}#mermaid-svg-MnTakInIWK0HzXQl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MnTakInIWK0HzXQl .arrowheadPath{fill:#333333;}#mermaid-svg-MnTakInIWK0HzXQl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MnTakInIWK0HzXQl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MnTakInIWK0HzXQl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MnTakInIWK0HzXQl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MnTakInIWK0HzXQl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MnTakInIWK0HzXQl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MnTakInIWK0HzXQl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MnTakInIWK0HzXQl .cluster text{fill:#333;}#mermaid-svg-MnTakInIWK0HzXQl .cluster span{color:#333;}#mermaid-svg-MnTakInIWK0HzXQl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MnTakInIWK0HzXQl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MnTakInIWK0HzXQl rect.text{fill:none;stroke-width:0;}#mermaid-svg-MnTakInIWK0HzXQl .icon-shape,#mermaid-svg-MnTakInIWK0HzXQl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MnTakInIWK0HzXQl .icon-shape p,#mermaid-svg-MnTakInIWK0HzXQl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MnTakInIWK0HzXQl .icon-shape .label rect,#mermaid-svg-MnTakInIWK0HzXQl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MnTakInIWK0HzXQl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MnTakInIWK0HzXQl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MnTakInIWK0HzXQl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Worker 集群(独立进程)
Master 集群(去中心化)
Master-1
槽位 0
Master-2
槽位 1
Master-3
槽位 2
注册中心
ZK / Etcd / JDBC
Worker-1
Worker-2
Worker-3
核心机制:Command ID 取余分片
DS 对工作流产生的 Command 按 ID 取余分片,决定由哪个 Master 处理:
槽位 = Command ID % Master 总数
例如 3 台 Master,Command ID 1001 → 1001 % 3 = 2,归槽位 2 的 Master 处理。所有 Master 同时工作,互不影响。
故障转移(failover)
某个 Master 挂掉后,它在注册中心的注册信息被移除,其他 Master 感知到后:
- 用分布式锁保证只有一个 Master 执行容错;
- 把挂掉 Master 的工作流实例 host 置为 null,创建
RECOVER_TOLERANCE_FAULT_PROCESS类型的 Command; - 其他 Master 捞到该 Command 后接管,遍历 DAG:已完成的节点保留,未完成的任务重新提交(重跑)。
单任务也能分片并行吗?
DS 没有"一个任务内部多片"的概念,但可以通过 DYNAMIC 动态节点实现:给一个集合(如 "0,1,2"),动态节点展开成多个并行子任务,每个子任务拿一个分片参数处理数据,最后汇合。
优点:
- 调度层去中心化,多 Master 分片并行,加节点既 HA 又提升调度能力;
- 工作流 DAG 可视化编排,擅长复杂依赖;
- 调度与执行解耦,Worker 独立部署,故障隔离好;
- 支持多租户、多任务类型、补数、重跑。
缺点:
- 组件多(Master/Worker/API/Alert + 注册中心 + 数据库),部署和运维成本较高;
- 轻量级场景下略显笨重。
2.4 SchedulerX:阿里云托管,企业级能力
SchedulerX 是阿里云商业化的分布式任务调度平台,同样采用去中心化、多 Server、无锁 架构。它可以理解为"XXL-Job 的内嵌执行端 + DS 的去中心化分片调度"的结合,并且服务端由阿里云托管、免运维。
#mermaid-svg-HXvpjOAmkspgXC9g{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HXvpjOAmkspgXC9g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HXvpjOAmkspgXC9g .error-icon{fill:#552222;}#mermaid-svg-HXvpjOAmkspgXC9g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HXvpjOAmkspgXC9g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HXvpjOAmkspgXC9g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HXvpjOAmkspgXC9g .marker.cross{stroke:#333333;}#mermaid-svg-HXvpjOAmkspgXC9g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HXvpjOAmkspgXC9g p{margin:0;}#mermaid-svg-HXvpjOAmkspgXC9g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HXvpjOAmkspgXC9g .cluster-label text{fill:#333;}#mermaid-svg-HXvpjOAmkspgXC9g .cluster-label span{color:#333;}#mermaid-svg-HXvpjOAmkspgXC9g .cluster-label span p{background-color:transparent;}#mermaid-svg-HXvpjOAmkspgXC9g .label text,#mermaid-svg-HXvpjOAmkspgXC9g span{fill:#333;color:#333;}#mermaid-svg-HXvpjOAmkspgXC9g .node rect,#mermaid-svg-HXvpjOAmkspgXC9g .node circle,#mermaid-svg-HXvpjOAmkspgXC9g .node ellipse,#mermaid-svg-HXvpjOAmkspgXC9g .node polygon,#mermaid-svg-HXvpjOAmkspgXC9g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HXvpjOAmkspgXC9g .rough-node .label text,#mermaid-svg-HXvpjOAmkspgXC9g .node .label text,#mermaid-svg-HXvpjOAmkspgXC9g .image-shape .label,#mermaid-svg-HXvpjOAmkspgXC9g .icon-shape .label{text-anchor:middle;}#mermaid-svg-HXvpjOAmkspgXC9g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HXvpjOAmkspgXC9g .rough-node .label,#mermaid-svg-HXvpjOAmkspgXC9g .node .label,#mermaid-svg-HXvpjOAmkspgXC9g .image-shape .label,#mermaid-svg-HXvpjOAmkspgXC9g .icon-shape .label{text-align:center;}#mermaid-svg-HXvpjOAmkspgXC9g .node.clickable{cursor:pointer;}#mermaid-svg-HXvpjOAmkspgXC9g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HXvpjOAmkspgXC9g .arrowheadPath{fill:#333333;}#mermaid-svg-HXvpjOAmkspgXC9g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HXvpjOAmkspgXC9g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HXvpjOAmkspgXC9g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXvpjOAmkspgXC9g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HXvpjOAmkspgXC9g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXvpjOAmkspgXC9g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HXvpjOAmkspgXC9g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HXvpjOAmkspgXC9g .cluster text{fill:#333;}#mermaid-svg-HXvpjOAmkspgXC9g .cluster span{color:#333;}#mermaid-svg-HXvpjOAmkspgXC9g div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HXvpjOAmkspgXC9g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HXvpjOAmkspgXC9g rect.text{fill:none;stroke-width:0;}#mermaid-svg-HXvpjOAmkspgXC9g .icon-shape,#mermaid-svg-HXvpjOAmkspgXC9g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXvpjOAmkspgXC9g .icon-shape p,#mermaid-svg-HXvpjOAmkspgXC9g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HXvpjOAmkspgXC9g .icon-shape .label rect,#mermaid-svg-HXvpjOAmkspgXC9g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXvpjOAmkspgXC9g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HXvpjOAmkspgXC9g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HXvpjOAmkspgXC9g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 业务应用集群(内嵌 agent)
SchedulerX Server 集群(阿里云托管,多副本)
Server-1
调度任务 A 组
Server-2
调度任务 B 组
Server-3
调度任务 C 组
业务应用-1
agent
业务应用-2
agent
业务应用-3
agent
核心机制:多 Server 分片、无锁、水平扩展
- 不同 Server 调度不同任务,无锁竞争,水平扩展,官方称支持百万级任务;
- 多副本 + 数据三备份,可容忍任意两个节点故障或整个数据中心断电,经历阿里双十一验证;
- 业务应用引入 agent(支持 Java/Go/Python/Shell/HTTP),Server 到点后主动调用 agent,agent 执行业务方法。
丰富的任务模型:
| 模型 | 说明 | 场景 |
|---|---|---|
| 单机 | 随机选一个 Worker 执行 | 普通定时任务 |
| 广播 Broadcast | 所有 Worker 跑同一方法 | 缓存刷新、配置重载 |
| 分片(静态/动态) | 多机分片处理 | 分库分表、分布式跑批 |
| Map | 根任务切块,分发多 Worker 并行 | 大数据集并行 |
| MapReduce | Map 分发 + 全部完成后 Reduce 聚合 | 轻量级分布式跑批 |
| 工作流 DAG | 可视化拖拽编排 | 多任务依赖 |
优点:
- 服务端免运维,开箱即用,企业级 SLA;
- 多 Server 无锁分片,支持百万级、秒级调度;
- 任务模型最全,一个平台覆盖分片、广播、MapReduce、工作流;
- 兼容 XXL-Job、ElasticJob、Spring Schedule、K8s Job。
缺点:
- 商业化产品,需要付费;
- 绑定阿里云生态,不适合私有化/多云部署。
三、核心概念辨析
3.1 统一的三阶段模型
任何一次任务调度,都可以拆成三个阶段:
#mermaid-svg-qDcVvilanombE9LR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qDcVvilanombE9LR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qDcVvilanombE9LR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qDcVvilanombE9LR .error-icon{fill:#552222;}#mermaid-svg-qDcVvilanombE9LR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qDcVvilanombE9LR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qDcVvilanombE9LR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qDcVvilanombE9LR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qDcVvilanombE9LR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qDcVvilanombE9LR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qDcVvilanombE9LR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qDcVvilanombE9LR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qDcVvilanombE9LR .marker.cross{stroke:#333333;}#mermaid-svg-qDcVvilanombE9LR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qDcVvilanombE9LR p{margin:0;}#mermaid-svg-qDcVvilanombE9LR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qDcVvilanombE9LR .cluster-label text{fill:#333;}#mermaid-svg-qDcVvilanombE9LR .cluster-label span{color:#333;}#mermaid-svg-qDcVvilanombE9LR .cluster-label span p{background-color:transparent;}#mermaid-svg-qDcVvilanombE9LR .label text,#mermaid-svg-qDcVvilanombE9LR span{fill:#333;color:#333;}#mermaid-svg-qDcVvilanombE9LR .node rect,#mermaid-svg-qDcVvilanombE9LR .node circle,#mermaid-svg-qDcVvilanombE9LR .node ellipse,#mermaid-svg-qDcVvilanombE9LR .node polygon,#mermaid-svg-qDcVvilanombE9LR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qDcVvilanombE9LR .rough-node .label text,#mermaid-svg-qDcVvilanombE9LR .node .label text,#mermaid-svg-qDcVvilanombE9LR .image-shape .label,#mermaid-svg-qDcVvilanombE9LR .icon-shape .label{text-anchor:middle;}#mermaid-svg-qDcVvilanombE9LR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qDcVvilanombE9LR .rough-node .label,#mermaid-svg-qDcVvilanombE9LR .node .label,#mermaid-svg-qDcVvilanombE9LR .image-shape .label,#mermaid-svg-qDcVvilanombE9LR .icon-shape .label{text-align:center;}#mermaid-svg-qDcVvilanombE9LR .node.clickable{cursor:pointer;}#mermaid-svg-qDcVvilanombE9LR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qDcVvilanombE9LR .arrowheadPath{fill:#333333;}#mermaid-svg-qDcVvilanombE9LR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qDcVvilanombE9LR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qDcVvilanombE9LR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qDcVvilanombE9LR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qDcVvilanombE9LR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qDcVvilanombE9LR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qDcVvilanombE9LR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qDcVvilanombE9LR .cluster text{fill:#333;}#mermaid-svg-qDcVvilanombE9LR .cluster span{color:#333;}#mermaid-svg-qDcVvilanombE9LR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qDcVvilanombE9LR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qDcVvilanombE9LR rect.text{fill:none;stroke-width:0;}#mermaid-svg-qDcVvilanombE9LR .icon-shape,#mermaid-svg-qDcVvilanombE9LR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qDcVvilanombE9LR .icon-shape p,#mermaid-svg-qDcVvilanombE9LR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qDcVvilanombE9LR .icon-shape .label rect,#mermaid-svg-qDcVvilanombE9LR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qDcVvilanombE9LR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qDcVvilanombE9LR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qDcVvilanombE9LR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 调度判断
时间到没到
② 触发调用
发出请求/下发
③ 业务执行
真正跑逻辑
- ① 调度判断:判断任务的触发时间是否到达;
- ② 触发调用:发出请求、把任务下发到执行端;
- ③ 业务执行:真正执行业务逻辑。
前两个阶段(①②)在调度端,第三个阶段(③)在执行端。
3.2 四个框架在三阶段的位置
| 框架 | ① 调度判断 | ② 触发调用 | ③ 业务执行 |
|---|---|---|---|
| XXL-Job | 调度中心(DB 锁,一个做) | 调度中心 | 执行器(业务应用) |
| ElasticJob | 业务节点(本地 cron) | 业务节点 | 业务节点 |
| DolphinScheduler | Master(分片) | Master | Worker(独立进程) |
| SchedulerX | Server(分片) | Server | 业务端 agent |
3.3 中心化 vs 去中心化
判断标准是"有没有一个统管全局的主":
- 中心化:存在一个全局主,其他节点是它的从属。无论主是固定的还是选举出来的,都属于中心化。
- 去中心化:没有全局主,多个节点对等分片。
注意:"自主选主"不等于"去中心化"。Raft、ZooKeeper 这类系统虽然能自主选主,但运行时仍然是单 Leader 主从结构,属于中心化的高可用(HA)。
3.4 推送 vs 自主
- 服务端推送:调度端判断到点后,主动把任务下发给执行端。XXL-Job、DS、SchedulerX 都是推送模式。
- 客户端自主:没有中心触发,节点本地定时、自己判断、自己执行。ElasticJob 是自主模式。
3.5 解耦 vs 合一
- 调度与执行解耦:调度端和执行端分离,可独立扩展、故障隔离。XXL-Job、DS、SchedulerX 都是解耦的。
- 调度与执行合一:调度和执行都在业务节点内。ElasticJob 是合一的。
四、四大框架横向对比
| 维度 | XXL-Job | ElasticJob | DolphinScheduler | SchedulerX |
|---|---|---|---|---|
| 架构模式 | 中心式调度 | 无中心(内嵌) | 去中心化多 Master | 去中心化多 Server |
| 调度与执行 | 分离 | 合一 | 分离 | 分离 |
| 调度层并行 | 单点(DB 锁) | 节点本地 | 多 Master 分片 | 多 Server 分片,无锁 |
| 任务下发方式 | 推送 | 自主(本地) | 推送 | 推送 |
| 数据分片 | 弱(分片广播) | 强(数据分片) | 动态节点实现 | 强(分片/Map/MapReduce) |
| 工作流编排 | 弱 | 无 | 强 | 强 |
| 可视化管理 | 强 | 弱 | 强 | 强 |
| 执行端形态 | 内嵌业务应用 | 业务节点 | 独立 Worker | 内嵌业务应用 |
| 部署方式 | 自建 | 自建 | 自建 | 云托管,免运维 |
| 规模上限 | 万级以下 | 中等 | 海量 | 百万级 |
| 成本 | 免费开源 | 免费开源 | 免费开源 | 商业付费 |
五、选型建议
| 场景 | 推荐框架 | 理由 |
|---|---|---|
| 任务量中小、想快速落地、团队小 | XXL-Job | 轻量、简单、开箱即用,社区成熟 |
| 单个任务数据量大、需要数据分片并行 | ElasticJob | 原生数据分片,无中心,弹性扩容 |
| 海量工作流、复杂 DAG 依赖、多租户 | DolphinScheduler | 去中心化多 Master,调度能力可水平扩展 |
| 企业级、规模大、要 SLA、在阿里云生态 | SchedulerX | 托管免运维,百万级,任务模型最全 |
选型时可以问自己三个问题:
- 任务规模多大?万级以下 XXL-Job 足够,万级以上考虑 DS / SchedulerX;
- 是"一堆独立任务"还是"一个任务要切数据"?后者优先 ElasticJob;
- 想自己运维还是接受云托管?要 SLA 且在云上 → SchedulerX。
六、总结
四大框架的架构差异,可以用四句话概括:
- XXL-Job:中心式调度 + DB 锁单点触发,调度中心与执行器分离,胜在简单易用;
- ElasticJob:无中心、jar 内嵌业务节点,本地定时 + 数据分片,胜在单任务并行;
- DolphinScheduler:去中心化多 Master + 独立 Worker,分片调度 + 故障转移,胜在工作流编排;
- SchedulerX:阿里云托管的去中心化多 Server,无锁分片 + 全任务模型,胜在企业级能力。