永恒编排函数 是永不结束的协调器函数。 如果要对聚合器使用 Durable Functions,以及需要无限循环的任何方案,它们非常有用。
永恒编排 是永不结束的编排。 如果要将持久业务流程用于聚合器和任何需要无限循环的方案,它们非常有用。
重要
目前,PowerShell Durable Task SDK 不可用。
调度历史记录
如 业务流程历史记录 主题中所述,Durable Task Framework 会跟踪每个函数业务流程的历史记录。 只要协调器功能安排新工作,此记录就会持续增长。 如果业务流程协调程序函数进入无限循环并持续计划工作,则历史记录可能会大幅增长,并导致严重的性能问题。 永恒编排概念旨在缓解需要无限循环的应用程序出现此类问题。
持久任务的软件开发工具包跟踪每个编排的历史记录。 只要编排计划新的工作,这个历史记录就会不断增长。 如果编排进入无限循环并持续安排任务,则历史记录可能会增长到非常大的规模,并导致严重的性能问题。 永恒编排概念旨在缓解需要无限循环的应用程序出现此类问题。
重置和重启
协调器函数通过调用continue-as-new协调触发器绑定的方法来重置其状态,而不是使用无限循环。 此方法采用 JSON 可序列化参数,该参数将成为下一个业务流程协调程序函数生成的新输入。
调用 continue-as-new时,业务流程实例会用新的输入值重启自身。 保留相同的实例 ID,但调度器函数的历史记录会重置。
编排通过在编排上下文上调用 continue-as-new 方法来重置其状态,而不是使用无限循环。 此方法采用 JSON 可序列化参数,该参数将成为下一代业务流程的新输入。
调用 continue-as-new时,业务流程实例会用新的输入值重启自身。 保留相同的实例 ID,但编排的历史记录会重置。
持续编排注意事项
在编排流程中使用 continue-as-new 方法时,请记住以下注意事项:
通过使用
continue-as-new方法重置业务流程协调程序函数时,Durable Task Framework 将维持相同的实例 ID,但会在内部创建并使用新的 执行 ID。 此执行 ID 不会在外部公开,但在调试业务流程执行时非常有用。在执行期间发生未经处理的异常时,业务流程将进入 失败 状态并终止执行。 在此状态下,从 try-catch 语句块中的
continue-as-new调用finally无法重新启动协调流程。
重要
如果在执行过程中业务流程遇到未捕获的异常,业务流程将进入“失败”状态并完成执行。 特别是,这意味着即使在 块中,调用finally也不会在未捕获异常的情况下重启业务流程。
在编排流程中使用 continue-as-new 方法时,请记住以下注意事项:
通过使用
continue-as-new方法重置业务流程时,该 Durable Task SDK 会维护相同的实例 ID,但在内部会创建并使用一个新的 执行 ID。 此执行 ID 不会在外部公开,但在调试业务流程执行时可能很有用。在执行期间发生未经处理的异常时,业务流程将进入 失败 状态并终止执行。 在此状态下,从 try-catch 语句块中的
continue-as-new调用finally无法重新启动协调流程。当业务流程调用
continue-as-new时,任何未完成任务的结果都会被丢弃。 例如,如果一个计时器被设置并且在计时器触发之前调用continue-as-new,则计时器事件将被丢弃。可以选择在
continue-as-new重启时保留未处理的外部事件。 在.NET和Java中,continue-as-new默认保留未处理的事件。 在 Python 中,除非continue_as_new,否则save_events=True不会保留事件。 在 JavaScript 中,continueAsNew需要参数saveEvents(true或false)来控制此行为。 在所有情况下,当业务流程下次调用waitForExternalEvent或者wait_for_external_event时,都会传递未处理事件。
重要
如果在执行过程中业务流程遇到未捕获的异常,业务流程将进入“失败”状态并完成执行。 特别是,这意味着即使在 块中,调用finally也不会在未捕获异常的情况下重启业务流程。
定期工作示例
永久编排的一种用例是能够无限期执行定期任务的代码。
[FunctionName("Periodic_Cleanup_Loop")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
await context.CallActivityAsync("DoCleanup", null);
// sleep for one hour between cleanups
DateTime nextCleanup = context.CurrentUtcDateTime.AddHours(1);
await context.CreateTimer(nextCleanup, CancellationToken.None);
context.ContinueAsNew(null);
}
注释
前面的 C# 示例适用于 Durable Functions 2.x。 对于 Durable Functions 1.x,必须使用 DurableOrchestrationContext 而不是 IDurableOrchestrationContext。 有关版本之间差异的详细信息,请参阅 Durable Functions 版本一文。
此示例与计时器触发的函数之间的差异在于清理操作的触发时机不是基于预定时间表。 例如,每小时运行一次函数的 CRON 计划会在 1:00、2:00、3:00 等时间运行,并可能会遇到重叠问题。 在此示例中,如果清理需要 30 分钟,则计划为 1:00、2:30、4:00 等,并且没有重叠的可能性。
public class PeriodicCleanupLoop : TaskOrchestrator<object?, object?>
{
public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
{
await context.CallActivityAsync("DoCleanup");
// sleep for one hour between cleanups
await context.CreateTimer(TimeSpan.FromHours(1), CancellationToken.None);
context.ContinueAsNew(null);
return null;
}
}
此示例与基于计时器的方法之间的差异在于,清理触发器时间不基于计划。 例如,每小时运行的计划在 1:00、2:00、3:00 等时间运行,并可能会遇到重叠问题。 在此示例中,如果清理需要 30 分钟,则计划为 1:00、2:30、4:00 等,并且没有重叠的可能性。
启动永久编排流程
使用 start-new 或 schedule-new 持久客户端方法启动持久化编排,就像对任何其他编排函数一样。
注释
如果需要确保单例持久编排正在运行,请在启动编排时维持相同的实例 id 。 有关详细信息,请参阅 实例管理。
[FunctionName("Trigger_Eternal_Orchestration")]
public static async Task<HttpResponseMessage> OrchestrationTrigger(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage request,
[DurableClient] IDurableOrchestrationClient client)
{
string instanceId = "StaticId";
await client.StartNewAsync("Periodic_Cleanup_Loop", instanceId);
return client.CreateCheckStatusResponse(request, instanceId);
}
注释
前面的代码适用于 Durable Functions 2.x。 对于 Durable Functions 1.x,请使用 OrchestrationClient 属性而不是 DurableClient 特性,并使用 DurableOrchestrationClient 参数类型而不是 IDurableOrchestrationClient参数类型。 有关版本差异的详细信息,请参阅 Durable Functions 版本。
使用计划新建客户端方法启动永久业务流程,就像对任何其他业务流程一样。
注释
如果需要确保单例持久编排正在运行,请在启动编排时维持相同的实例 id 。
string instanceId = "StaticId";
await client.ScheduleNewOrchestrationInstanceAsync(
"PeriodicCleanupLoop",
null,
new StartOrchestrationOptions { InstanceId = instanceId });
退出永恒编排
如果编排器函数需要最终完成,请不要调用 ContinueAsNew 并使函数退出。
如果业务流程协调程序函数处于无限循环中并且需要停止,请使用业务流程客户端绑定的终止 API 来停止它。 有关详细信息,请参阅 实例管理。
如果业务流程需要最终完成,请不要调用 continue-as-new 业务流程并让业务流程退出。
如果业务流程处于无限循环中并且需要停止,请使用持久任务客户端上的 终止 API 来停止它。
await client.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");