工作流
本文档从底层详细说明 Dapr 工作流协议与运行时约定。面向的读者是构建工作流 Worker 的 SDK 作者以及演进 Dapr 边车工作流引擎的运行时维护者。
概述
Dapr 工作流采用"边车即调度器"模式:Dapr 运行时(边车)作为工作流引擎,应用 SDK 作为工作流 Worker。所有控制与执行流量均通过 gRPC 传输。
协议界面分为两类:
- 管理 API(通过 SDK 可访问的标准 Dapr gRPC):
- 启动、终止、暂停、恢复、重新运行、清除以及查询工作流实例。
- 执行 API(Task Hub 协议):
- Worker 侧接口,用于接收编排/活动工作项并上报完成(例如通过
TaskHubSidecarService)。
- Worker 侧接口,用于接收编排/活动工作项并上报完成(例如通过
核心组件
工作流引擎(Dapr 边车)
管理工作流状态转换、历史持久化、编排与活动任务的调度,以及可靠交付语义。默认情况下,利用 Dapr Actors 作为后端,实现可持久化、分区的执行。
工作流 Worker(应用 SDK)
连接到边车,轮询编排与活动工作项,执行用户定义的逻辑,并将结果、失败与心跳返回给引擎。编排逻辑必须是确定性的;活动逻辑无需是确定性的。
编排
定义工作流的确定性协调器。引擎通过重放历史来驱动编排,重建状态并调度出站任务(活动、子编排、定时器、外部事件)。
活动
原子工作单元。活动执行保证至少一次,并将结果或失败报告回引擎。建议幂等,并在上下文中提供任务执行标识符以辅助实现幂等。
状态存储与后端
工作流历史与状态持久化保存。引擎通常在所选持久化层上实现任务中心模式,并使用 Dapr Actors 作为默认的可靠性底层。
执行模型
Dapr 工作流基于持久任务框架(DTFx)执行语义:
基于重放的执行
编排器从其事件历史重放,以重建确定性状态。所有非确定性操作(时间、随机值、I/O)必须由引擎中介(例如通过定时器、活动调用、外部事件)。
确定性编排器
除引擎中介效果外,编排器代码必须无副作用。控制流在重放期间必须可复现。
活动至少一次交付、状态提交恰好一次
活动可能被多次传递。引擎确保工作流状态提交是幂等的,并恰好一次应用。
边车即调度器
边车拥有调度权,并在向 Worker 分发工作前持久化所有历史/事件。从引擎视角看,Worker 是无状态执行器。
协议界面
- 管理 API(标准 Dapr gRPC)
- 启动工作流:创建并持久化初始历史事件;返回实例元数据。
- 终止 / 暂停 / 恢复:通过持久化的控制事件驱动生命周期转换。
- 查询:检索实例状态、历史、输出、失败详情及自定义元数据。
- 重新运行:从历史事件启动新的工作流实例。
- 清除:主动清除工作流历史与状态。
注意:关于确切的 RPC 形状、错误码和语义,参见 管理 API 规范。
- 执行 API(Task Hub 协议)
- 轮询工作:Worker 获取编排与活动工作项。
- 完成 / 失败工作:Worker 报告完成结果或失败;引擎将这些追加到历史并推进编排进度。
- 心跳 / 租约:用于长时间运行的活动与协作式重新平衡的可选机制。
- 定时器与外部事件:作为历史事件传递给编排,以保持重放的确定性。
注意:参见定义了
TaskHubSidecarService约定、负载架构与排序规则的 执行 API 规范。
请求与运行时生命周期
- 启动工作流
- 客户端通过管理 API 调用
StartWorkflow。 - 引擎持久化初始事件(例如
ExecutionStarted)并实例化实例。
- 编排器执行(重放驱动)
- 引擎重放编排历史以重建状态。
- 编排器通过发布命令调度效果(活动、子编排、定时器),引擎将其持久化为新的历史事件。
- 活动分发与执行
- 引擎将活动工作项分发给 Worker。
- Worker 运行活动(可能重试,并至少一次交付)。
- Worker 以完成(结果)或失败响应;引擎追加到历史。
- 定时器与外部信号
- 引擎将定时器触发或外部事件记录作为历史条目交付。
- 编排器在下次重放时确定性地消费这些事件。
- 推进与检查点
- 每一步追加到历史日志并推进编排状态。
- 引擎保证编排状态的幂等性与恰好一次提交。
- 完成
- 编排返回输出(成功)或失败(异常详情)。
- 最终状态与输出被持久化;状态查询反映终止状态。
协议原则
- GRIEF(GRpc IntErFace):所有 Worker/引擎与客户端/引擎通信均为 gRPC。
- 基于重放的编排:通过历史重放保证确定性。
- 活动至少一次交付:活动可能重新执行;应设计为幂等。
- 引擎中介的效果:所有非确定性/时间/IO 均通过引擎流动以保持重放安全。