你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

永恒的编排

永恒编排 是协调器函数,通过使用 continue-as-new API 定期重置自身历史记录而持续运行。 它们对于聚合器、周期性后台作业以及任何需要无限循环而不产生无界历史增长的 Durable Functions 场景非常有用。

没有 continue-as-new 时,一个无限循环的协调器在每次调度任务时都会累积 业务流程历史记录,最终导致性能问题和内存消耗过度。 永恒的业务流程模式通过重置每次迭代的历史记录来解决此问题。

注释

永恒编排代码样例提供适用于 C#、JavaScript、Python 和 Java。 PowerShell 不支持 continue-as-new

本文内容:

永久业务流程是一种通过使用 continue-as-new API 定期重置自身历史来无限期运行的业务流程。 它们对于聚合器、周期性后台任务以及任何需要无限循环但不会导致历史记录无限增长的场景都非常有用。

如果没有 continue-as-new,永久循环的编排在每个计划任务中都会累计历史记录,最终导致性能问题和过多的内存使用。 永恒的业务流程模式通过重置每次迭代的历史记录来解决此问题。

重要

目前,PowerShell Durable Task SDK 不可用。

本文内容:

continue-as-new 的工作原理

协调器函数通过调用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 不会在外部公开,但在调试业务流程执行时非常有用。

  • 在执行期间发生未经处理的异常时,业务流程将进入 失败 状态并终止执行。 若从 finally 块内部调用 continue-as-new,则在未捕获的异常之后不会重新启动业务流程。

  • 当业务流程调用 continue-as-new 时,任何未完成任务的结果都会被丢弃。 例如,如果一个计时器被设置并且在计时器触发之前调用continue-as-new,则计时器事件将被丢弃。

  • 可以选择在 continue-as-new 重启时保留未处理的外部事件。 在 C# 中, ContinueAsNew 默认保留未处理的事件。 在 Java 中,continueAsNew默认也会保留事件。 在 Python 中,除非 continue_as_new,否则 save_events=True 不会保留事件。 在 JavaScript 中, continueAsNew 需要参数 saveEventstruefalse)来控制此行为。

在编排流程中使用 continue-as-new 方法时,请记住以下注意事项:

  • 当业务流程使用 continue-as-new 方法重置时,持久任务 SDK 保留相同的实例 ID,但在内部创建并使用新的执行 ID。 此执行 ID 不会在外部公开,但在调试业务流程执行时可能很有用。

  • 在执行期间发生未经处理的异常时,业务流程将进入 失败 状态并终止执行。 若从 finally 块内部调用 continue-as-new,则在未捕获的异常之后不会重新启动业务流程。

  • 当业务流程调用 continue-as-new 时,任何未完成任务的结果都会被丢弃。 例如,如果一个计时器被设置并且在计时器触发之前调用continue-as-new,则计时器事件将被丢弃。

  • 可以选择在 continue-as-new 重启时保留未处理的外部事件。 在.NET和Java中,continue-as-new默认保留未处理的事件。 在 Python 中,除非 continue_as_new,否则 save_events=True 不会保留事件。 在 JavaScript 中, continueAsNew 需要参数 saveEventstruefalse)来控制此行为。 在所有情况下,当业务流程下次调用waitForExternalEvent或者wait_for_external_event时,都会传递未处理事件。

定期工作示例

永久业务流程的一个常见用例是周期性后台工作,例如清理作业。

为什么不使用计时器触发器? 基于 CRON 的计时器触发器在固定时间运行,无论上一次运行是否已完成。 永久业务流程会等待工作完成后再调度下一次迭代,因此运行永远不会重叠。

Approach 计划(1 小时间隔,30 分钟作业) 重叠风险
计时器触发器 (CRON) 1:00, 2:00, 3:00 是 — 如果作业超出间隔
永恒编排 1:00, 2:30, 4:00 否 — 下一次运行等待完成
[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);
}
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;
    }
}

启动永久业务流程

使用 start-newschedule-new durable client 方法启动一个无限期编排,就像任何其他编排函数一样。 若要确保一次只运行一个实例,请使用固定实例 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);
}

使用 schedule-new 客户端方法启动永续编排,就像任何其他编排一样。 若要确保一次只运行一个实例,请使用固定实例 ID。 有关更多信息,请参阅单例业务流程

string instanceId = "StaticId";
await client.ScheduleNewOrchestrationInstanceAsync(
    "PeriodicCleanupLoop",
    null,
    new StartOrchestrationOptions { InstanceId = instanceId });

从永久业务流程退出

如果编排器函数需要最终完成,请不要调用 continue-as-new 并使函数退出。

如果业务流程协调程序函数处于无限循环中并且需要停止,请使用业务流程客户端绑定终止 API 来停止它。

await client.TerminateAsync(instanceId, "Cleanup no longer needed");

有关详细信息,请参阅 实例管理

如果业务流程最终需要完成,请不要调用 continue-as-new 并让业务流程退出。

如果业务流程处于无限循环中并且需要停止,请使用持久任务客户端上的 终止 API 来停止它。

await client.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");

后续步骤