3. 任务调度核心模型:四要素深度解析
任何 DAG 任务调度系统,无论其架构如何演进,都建立在四个核心抽象之上:任务(Task)、依赖(Dependency)、执行器(Executor)、调度器(Scheduler)。这四个要素构成了调度系统的“元模型”,决定了系统的能力边界与扩展方向。
理解四要素的本质及其相互关系,是设计高可用、可扩展调度系统的第一步。下面我们逐一拆解每个要素的职责、属性与设计要点。
3.1 任务(Task):最小执行单元
任务是调度系统中最基本的执行单元,它封装了一段可执行的逻辑。在 DAG 模型中,每个节点就是一个 Task。任务的粒度决定了系统的灵活性:粒度过粗会导致资源浪费,粒度过细则会增加调度开销。
| 属性维度 | 典型字段 | 说明 |
|---|---|---|
| 标识 | taskId, name, version | 全局唯一,用于追踪与重试 |
| 执行定义 | command, image, scriptRef | 描述“做什么”,可以是 Shell、Python、容器镜像 |
| 资源需求 | cpu, memory, gpu, disk | 调度器据此进行资源匹配与装箱 |
| 超时与重试 | timeout, maxRetries, retryDelay | 防止任务“死锁”或无限重试 |
| 状态机 | PENDING → RUNNING → SUCCESS/FAILED | 驱动任务生命周期流转 |
设计要点:任务定义应保持“无状态”和“幂等”。无状态意味着任务不依赖本地磁盘数据;幂等意味着重复执行相同任务不会产生副作用。这两点是实现失败重试和水平扩展的前提。
3.2 依赖(Dependency):DAG 的骨架
依赖定义了任务之间的执行顺序与数据流向。没有依赖,任务就是孤立的;有了依赖,任务才形成有向无环图。依赖管理是调度系统最核心的复杂度来源。
依赖通常分为两类:
- 硬依赖(Hard Dependency):上游任务必须成功,下游任务才能启动。这是 DAG 调度中最常见的依赖类型。
- 软依赖(Soft Dependency):上游任务的状态影响下游任务的启动条件,但不强制要求成功。例如“上游完成即可,无论成功或失败”。
依赖关系的表示方式直接影响调度器的判断效率:
- 邻接表:每个任务记录其上游任务列表(predecessors)和下游任务列表(successors)。适合动态图遍历。
- 入度计数:每个任务记录其尚未满足的上游依赖数量。入度归零时,任务即可被调度。这是拓扑排序的经典实现。
边界拆分原则:依赖管理应独立于任务执行逻辑。调度器只关心“依赖是否满足”,不关心任务内部做了什么。这种分离使得依赖检查可以提前进行,甚至可以在任务提交阶段就完成静态校验。
3.3 执行器(Executor):任务的“工人”
执行器是实际运行任务的组件。它从调度器接收任务指令,在指定的资源环境中执行任务,并将执行结果(状态、日志、输出)上报给调度器。
执行器的设计模式决定了系统的资源利用率和隔离性:
| 模式 | 资源模型 | 隔离性 | 典型场景 |
|---|---|---|---|
| 进程级 | 每个任务启动一个进程 | 低(共享宿主环境) | 简单脚本、开发环境 |
| 容器级 | 每个任务运行在独立容器中 | 高(资源隔离) | 生产环境、多租户场景 |
| 线程级 | 线程池复用 | 极低 | 轻量计算、IO 密集型 |
常见误区:将执行器与调度器混为一谈。调度器负责“决策”——决定哪个任务该运行;执行器负责“执行”——实际运行任务。两者职责分离后,调度器可以专注于全局最优调度策略,执行器可以专注于资源管理和任务生命周期。
3.4 调度器(Scheduler):决策中枢
调度器是整个系统的“大脑”。它维护 DAG 的全局状态,监听任务完成事件,触发依赖检查,决定下一个可调度任务,并将任务分发给合适的执行器。
调度器的核心职责可以概括为三个循环:
- 事件循环:监听任务状态变更(完成、失败、超时),更新 DAG 状态。
- 拓扑循环:遍历 DAG,检查入度归零的任务,生成可调度任务列表。
- 分发循环:根据资源约束和调度策略(如 FIFO、优先级、公平调度),将任务分配给执行器。
调度器的设计难点在于:
- 并发控制:多个任务同时完成时,如何避免重复调度同一个下游任务?
- 状态一致性:调度器宕机后,如何恢复 DAG 的中间状态?
- 调度延迟:从任务完成到下游任务启动的时间间隔,应尽可能短。
3.5 四要素协作关系
下面这张图展示了四个要素在 DAG 调度系统中的协作流程:
3.6 边界拆分:为什么四要素必须分离?
很多初期的调度系统将四要素混在一起:任务定义中包含了调度逻辑,执行器里嵌入了依赖检查。这种“大泥球”架构在规模增长后迅速暴露出问题:
- 无法独立扩展:任务量增加时,需要同时扩容调度器和执行器,资源浪费。
- 难以故障隔离:执行器崩溃可能导致调度状态丢失。
- 调度策略僵化:修改调度算法需要重新部署整个系统。
四要素分离后的架构优势:
| 要素 | 独立演进能力 | 故障影响范围 |
|---|---|---|
| Task | 新增任务类型无需修改调度器 | 单个任务失败不影响其他任务 |
| Dependency | 支持动态依赖、条件依赖等复杂模式 | 依赖定义错误仅影响本 DAG |
| Scheduler | 可替换调度策略(FIFO/优先级/公平) | 调度器宕机可通过 Leader 选举恢复 |
| Executor | 可对接不同资源池(K8s/YARN/本地) | 单个 Executor 崩溃由调度器重新分发 |
架构启示:四要素的边界拆分,本质上是将“做什么”(Task)、“何时做”(Dependency)、“谁来做”(Executor)、“怎么做决策”(Scheduler)四个关注点解耦。这种拆分使得每个要素都可以独立优化、独立部署、独立扩展,是分布式系统“关注点分离”原则在调度领域的经典实践。