任务编排与 DAG
前面几篇讲的,基本还是“一个任务,一条线”这类模型。但真实业务很快就会遇到更复杂的情况:
- 有些步骤必须串行
- 有些步骤可以并行
- 有些步骤依赖多个前置结果
- 某一步失败后,只想重跑它和它后面的分支
这时候再把流程硬编码成一大串 if/else 或顺序函数,就会越来越难维护。
这就是任务编排存在的意义。
什么是 DAG
DAG 是 Directed Acyclic Graph,中文叫 有向无环图。
拆开理解:
- 有向:节点之间有依赖方向
- 无环:不能出现 A 依赖 B,B 又绕回依赖 A
在任务系统里,可以把它理解成:
一个任务不是单步执行,而是由多个节点按照依赖关系组织成的一张图。
一个直观例子
还是用“合同解析任务”举例。用户上传合同后,我们希望执行:
- 预处理文件
- OCR 识别文本
- 图片语义分析
- 提取结构化字段
- 做规则校验
- 建索引并入库
- 通知用户
其中:
OCR和图片语义分析可以并行提取结构化字段必须等前面两个都完成通知用户必须等最终入库完成
这就非常适合用 DAG 表达:
flowchart TD
A[上传文件] --> B[预处理]
B --> C[OCR]
B --> D[图片语义分析]
C --> E[字段提取]
D --> E
E --> F[规则校验]
F --> G[建索引并入库]
G --> H[通知用户]
这张图比“写一大段代码说明流程”更直观,因为它直接展示了:
- 谁依赖谁
- 哪些节点能并行
- 最终在哪个节点汇合
为什么任务编排不能只靠代码硬写
当然可以把上面的流程直接写成程序,但一旦业务变化,问题就来了。
例如今天产品说:
- OCR 前面要加一个“文件安全检查”节点
- 通知前还要多加一个“生成摘要”节点
- 某个租户不需要图片语义分析
如果你把流程写死在代码里,每次都要改逻辑、改依赖、改条件分支,最后会越来越难维护。
而 DAG 的价值就在于:
- 结构显式化:依赖关系一眼就能看懂
- 天然支持并行:没有依赖的节点可以同时跑
- 支持局部重跑:失败时不一定要从头开始
- 易于可视化:产品、研发、运维都看得懂
- 适合配置化:流程可以做成 DSL 或数据库配置
编排器到底做什么
有了 DAG 以后,还需要一个“编排器”来驱动整个图运行。
编排器通常要做这些事:
- 找出当前所有可执行节点
- 把可执行节点派发给 Worker
- 等节点完成后更新状态
- 解锁下游节点
- 判断整个工作流是否结束
可以用下面这个简化流程理解:
flowchart LR
A[创建工作流实例] --> B[找出无依赖节点]
B --> C[派发给 Worker]
C --> D[节点完成并回写状态]
D --> E{是否解锁新节点}
E -- 是 --> C
E -- 否 --> F{是否全部完成}
F -- 否 --> C
F -- 是 --> G[工作流结束]
所以编排器本质上不是“做业务”,而是“调度节点之间的关系”。
一个节点应该具备哪些信息
把 DAG 真正落地时,每个节点一般至少要有这些元信息:
| 字段 | 作用 |
|---|---|
nodeId |
节点标识 |
type |
节点类型,如 ocr、extract |
dependsOn |
依赖哪些前置节点 |
timeout |
超时时间 |
retryPolicy |
重试策略 |
input |
输入参数来源 |
output |
输出结果位置 |
例如一个极简 DSL 可以长这样:
nodes:
- id: preprocess
type: preprocess
dependsOn: []
- id: ocr
type: ocr
dependsOn: [preprocess]
- id: image_analysis
type: image_analysis
dependsOn: [preprocess]
- id: extract_fields
type: extract_fields
dependsOn: [ocr, image_analysis]
这时候流程就从“硬编码逻辑”变成了“数据驱动逻辑”。
简单案例:AI 内容生成工作流
以 AI 报告生成为例,也很适合 DAG。
任务可以拆成:
- 拉取原始数据
- 并行生成摘要、图表、关键词
- 合并结果生成完整报告
- 导出 PDF
- 通知用户下载
这里“摘要、图表、关键词”三步就是天然并行节点。
如果用 DAG,你就能做到:
- 提升整体耗时表现
- 某个分支失败时只重跑该分支
- 更容易把每一步耗时单独统计出来
DAG 和消息队列是什么关系
很多人会把这两个概念混在一起,其实它们分工不同:
- 消息队列 负责传递任务与节点执行消息
- DAG 编排器 负责决定“下一个该跑谁”
可以简单理解为:
- 队列是运输系统
- DAG 是交通规则与调度图
没有 DAG,你只能做简单任务流;没有队列,大规模节点执行又不稳定。
什么时候值得上 DAG
下面这些情况,通常说明你已经值得上编排了:
- 任务步骤超过 3 到 5 个
- 某些步骤可以并行
- 不同任务流只是在节点组合上不同
- 经常需要“从某一步继续跑”
- 希望把流程可视化给研发或运营看
如果只是一个很短的线性流程,直接顺序执行未必有必要引入完整编排系统。
初学者最容易踩的坑
1. 节点切得过细
每一步都拆成节点,会让编排成本和状态复杂度飙升。
2. 节点切得过粗
一整个大流程只有一个节点,那又失去了并行和重跑的价值。
3. 没有明确定义节点输入输出
最后节点之间靠共享全局变量或临时表通信,会非常难维护。
4. 忘了“无环”
一旦出现循环依赖,编排器就没法判断执行顺序。
这一篇要记住的核心点
- DAG 适合表达多步骤、有依赖、可并行的任务流
- 编排器负责调度依赖关系,队列负责运输执行消息
- DAG 的最大价值是:并行、重跑、可视化、配置化
下一篇继续补上另一个关键问题:这些任务和节点的状态,应该存在哪里,前端又该怎么把进度展示出来。