4. 任务编排模型:DAG(有向无环图)任务模型
在IAM平台的异步任务编排核心引擎中,任务之间的依赖关系与执行顺序不能依赖简单的线性队列。DAG(Directed Acyclic Graph,有向无环图)是业界公认的标准任务编排模型,它能够精确描述任务间的依赖约束,同时保证拓扑排序后不存在循环等待或死锁。
4.1 DAG任务模型定义
DAG由一组节点(Vertex)和一组有向边(Directed Edge)组成,且图中不存在任何环路。在IAM任务编排场景中:
- 节点:代表一个可执行的原子任务单元,例如“同步LDAP用户”、“校验Token权限”、“更新角色缓存”。
- 有向边:代表任务间的依赖关系,方向从前置任务指向后置任务,表示后置任务必须等待前置任务完成后才能开始执行。
核心约束: DAG中不允许存在环路。如果任务A依赖任务B,任务B又依赖任务A,则无法确定执行起点,引擎将拒绝提交该编排。
4.2 节点与边定义
在IAM引擎中,每个节点和边都携带丰富的元数据,用于权限校验、重试策略和状态追踪。下表列出了节点与边的核心属性:
| 元素 | 属性 | 类型 | 说明 |
|---|---|---|---|
| 节点 | nodeId | String | 全局唯一任务标识 |
| taskType | Enum | 任务类型:SYNC、VALIDATE、NOTIFY、ROLLBACK | |
| requiredPermission | String | 执行该任务所需的IAM权限标识 | |
| maxRetries | int | 最大重试次数(默认3次) | |
| timeoutSeconds | int | 任务超时时间(默认300秒) | |
| 边 | fromNodeId | String | 前置任务节点ID |
| toNodeId | String | 后置任务节点ID | |
| condition | String | 可选条件表达式,满足条件才触发下游 |
4.3 任务依赖关系解析
引擎在接收到DAG定义后,会执行依赖关系解析,主要包含以下步骤:
- 入度计算:遍历所有边,统计每个节点的入度(指向该节点的边数)。入度为0的节点即为起始节点。
- 依赖校验:检查每个节点的前置任务是否都在DAG中定义,防止出现“悬空依赖”。
- 权限聚合:从起始节点开始,沿边方向聚合路径上的权限要求,用于后续的权限校验。
- 环路检测:在拓扑排序过程中,如果最终排序结果中的节点数少于DAG总节点数,则说明存在环路,引擎拒绝执行。
实践建议: 在IAM平台中,依赖解析阶段应同时完成“权限预校验”。如果当前用户对DAG中某个节点不具备执行权限,应在提交阶段直接拒绝,而不是等到运行时才报错。
4.4 拓扑排序原理
拓扑排序(Topological Sorting)是DAG编排引擎的核心算法,它能够将DAG中的所有节点排列成一个线性序列,使得对于每一条有向边 (u → v),节点 u 在序列中都出现在节点 v 之前。常用的实现算法是Kahn算法(基于入度)。
Kahn算法伪代码如下:
function topologicalSort(graph):
// 1. 初始化入度表
inDegree = {node: 0 for node in graph.nodes}
for edge in graph.edges:
inDegree[edge.to] += 1
// 2. 将入度为0的节点加入队列
queue = [node for node in graph.nodes if inDegree[node] == 0]
result = []
// 3. 循环处理队列
while queue is not empty:
node = queue.pop(0)
result.append(node)
for neighbor in graph.getOutNeighbors(node):
inDegree[neighbor] -= 1
if inDegree[neighbor] == 0:
queue.append(neighbor)
// 4. 检查是否所有节点都已排序
if len(result) != len(graph.nodes):
throw "Graph contains a cycle"
return result
拓扑排序的时间复杂度为 O(V + E),其中 V 为节点数,E 为边数。在IAM平台的典型场景中,单个DAG的节点数通常不超过200个,该算法可在毫秒级完成。
4.5 DAG任务编排示例
下面以一个IAM权限同步场景为例,展示DAG的结构与拓扑排序结果。该场景包含5个任务节点:
4.6 拓扑排序在IAM中的特殊处理
在IAM平台中,拓扑排序除了确定执行顺序外,还需要额外处理以下场景:
- 并行度控制:入度为0的节点可以并行执行,但引擎需要根据线程池大小和系统负载动态控制并行度,防止资源耗尽。
- 条件分支:某些边的condition属性可能为“仅当前置任务执行成功”或“仅当前置任务执行失败”,拓扑排序需要根据运行时状态动态调整后续节点的入度。
- 权限传播:如果父节点具有高权限,子节点可能继承部分权限。拓扑排序过程中需要维护一个“权限上下文栈”,确保下游节点能够访问上游的临时凭证。
常见陷阱: 不要将拓扑排序与任务调度混为一谈。拓扑排序只负责确定依赖顺序,不负责决定何时执行。实际调度由引擎的调度器根据资源水位和优先级策略触发。