Durable Functions 的 Orchestration 方法是用 [FunctionName] 标记、参数为 IDurableOrchestrationContext 的 async Task 函数,仅通过 Durable 特定 API(如 CallActivityAsync)协调任务,自动持久化状态并支持重入。
什么是 Durable Functions 的 Orchestration 方法
Durable Functions 的核心是
,它不是普通 C# 方法,而是一个用
标记、参数类型为
(或
)的函数。它不直接处理 HTTP 或事件触发,只负责协调子任务执行顺序、状态保存与重入恢复。
关键点:该方法必须是
,且只能通过
、
、
等 Durable 特定 API 驱动流程——不能用
或直接 new Thread()。
如何正确编写一个带状态的编排函数
状态不是靠类字段或静态变量维持,而是由 Durable Task Framework 在每次
或 checkpoint 时自动序列化上下文中的局部变量(包括 await 后的变量值)并持久化到存储(如 Azure Storage Table + Blob)。所以写法上要“接受重入”:
所有非幂等操作(如发邮件、写 DB)必须放在
中,不能在 orchestration 方法体里直写
避免使用
,改用
,否则重放时值会变
条件分支(if/else)、循环(while)、异常捕获(try/catch)都允许,但所有 await 点都可能成为 checkpoint 位置
若需长期保持中间数据,应存入
或传给 activity 做外部存储
示例片段:
}
C知道
CSDN推出的一款AI技术问答工具
下载
为什么本地调试时状态总“丢失”或反复执行
常见现象:断点进 orchestration 函数多次、日志重复打印、timer 看似没生效——本质是 Durable 的重放机制被误判为 bug。Orchestration 函数每次从 checkpoint 恢复时都会完整重跑,直到遇到下一个 await 的 Durable API 才暂停。
检查是否在
前有非 Durable 的异步调用(如
),这会导致框架无法控制执行点,破坏重放一致性
确保 local.settings.json 中
指向可用的 Azurite 或真实 Storage 账户;空连接字符串会让框架退化为内存模式,重启即丢状态
不要在 orchestration 中修改外部可变对象(如传入的 List 引用),因为重放时对象状态不还原
使用
可区分首次执行和重放,仅在
时打关键日志或埋点
Activity 和 Sub-orchestration 怎么选
两者都用于拆分逻辑,但语义和用途不同:
:适合短时、无状态、可重试的原子操作(如查数据库、调第三方 API、转换 JSON)。失败默认重试,超时可配
:适合需要自身生命周期管理的长流程(如“退款流程”本身含多个步骤+人工审批+超时补偿),它有自己的历史表、可独立查询/终止/raise event
Activity 不能调用其他 Activity 或 Sub-orchestration;Sub-orchestration 可以,但它不能被 HTTP 触发,只能被 orchestration 调用
性能上 Activity 更轻量;Sub-orchestration 多一层调度开销,但提供更强的状态封装能力
简单原则:能用 Activity 解决的,别升格为 Sub-orchestration;需要跨多个业务域协同、或需单独监控/重试策略的,才考虑拆 Sub-orchestration。
真正难的是设计 checkpoint 边界和 error recovery 路径——比如支付失败后该回滚库存还是冻结订单,这些逻辑不会自动产生,得靠你在 activity 返回值和 orchestration 的 if 分支里显式编码。
Orchestration[FunctionName]IDurableOrchestrationContextIDurableOrchestrationContextBaseasync Taskcontext.CallActivityAsync() context.CreateTimer()context.WaitForExternalEvent()await Task.Delay()context.ContinueAsNew()CallActivityAsyncDateTime.Nowcontext.CurrentUtcDateTimecontext.SetCustomStatus()public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var input = context.GetInput();
var orderId = input.OrderId;
// ✅ 安全:重放时 context.CurrentUtcDateTime 返回相同值
var now = context.CurrentUtcDateTime;
var deadline = now.AddHours(1);
// ✅ 安全:activity 是幂等单元
await context.CallActivityAsync("ValidateOrder", input);
// ⚠️ 危险:下面这行如果写成 DateTime.UtcNow 会导致重放不一致
// var unsafeNow = DateTime.UtcNow;
if (await context.CreateTimer(deadline, CancellationToken.None))
{
await context.CallActivityAsync("FulfillOrder", orderId);
} CallActivityAsyncHttpClient.GetAsyncStorageConnectionStringcontext.IsReplaying!context.IsReplayingCallActivityAsyncRetryOptionsCallSubOrchestratorAsync