永恆的管弦樂編曲

永續協調流程是會無限期執行的協調器函數,並使用 continue-as-new API 定期重設自己的歷程記錄。 它們對聚合器、定期背景工作,以及任何需要無限迴圈且沒有無限歷史成長的 Durable Functions情境都很有用。

若沒有 continue-as-new,一個無限循環的編排器會隨著每個排程任務累積編 排歷史 ,最終導致效能問題和過度記憶體使用。 永恆編排模式透過重置每次迭代的歷史來解決此問題。

備註

Eternal orchestration 程式碼範例可用於 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 參數,該參數成為下一代 Orchestrator 函式產生的新輸入。

當您呼叫 continue-as-new 時,協調流程執行個體會使用新的輸入值重新啟動自身。 同一個實例 ID 會被保留,但 orchestrator 函式的歷史會被重置。

編排不使用無限迴圈,而是透過在編排上下文中呼叫 continue-as-new 方法來重置狀態。 此方法會取一個可序列化的 JSON 參數,該參數成為下一代編排產生的新輸入。

當您呼叫 continue-as-new 時,協調流程執行個體會使用新的輸入值重新啟動自身。 相同的實例 ID 會被保留,但編排的歷史會被重置。

永恆編排考量因素

在協調流程中使用 continue-as-new 方法時,請留意下列注意事項:

  • 當使用此 continue-as-new 方法重置編排器函式時,Durable Task Framework 會保留相同的實例 ID,但會內部建立並使用新的 執行 ID。 這個執行 ID 不會對外公開,但在除錯編排執行時非常有用。

  • 當執行期間發生未處理的例外狀況時,協調流程會進入 失敗 狀態,而且執行會終止。 從 continue-as-new 區塊呼叫 finally不會在未攔截的例外狀況之後重新啟動協調流程。

  • 當編排呼叫 continue-as-new時,任何未完成任務的結果都會被丟棄。 例如,如果已排程計時器,且在計時器觸發前呼叫 continue-as-new,則該計時器事件會遭到捨棄。

  • 您可以選擇在 continue-as-new 重新啟動之間,保留未處理的外部事件。 在 C# 中,預設 ContinueAsNew 保留未處理的事件。 在Java中,continueAsNew 預設也會保留事件。 在Python中,continue_as_new 除非 save_events=True,否則不會保留事件。 在 JavaScript 中, continueAsNew 需要一個 saveEvents 參數(truefalse)來控制此行為。

在協調流程中使用 continue-as-new 方法時,請留意下列注意事項:

  • 當透過此 continue-as-new 方法重置編排時,持久任務 SDK 會維持相同的實例 ID,但內部會建立並使用新的 執行 ID 。 此執行識別碼不會在外部公開,但在偵錯協調流程執行時可能很有用。

  • 當執行期間發生未處理的例外狀況時,協調流程會進入 失敗 狀態,而且執行會終止。 從 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 參數(truefalse)來控制此行為。 在所有情況下,當編排下一次呼叫 waitForExternalEventwait_for_external_event時,未處理的事件都會被傳送。

定期工作範例

永續協調流程的一個常見使用案例是定期背景工作,例如清除作業。

為什麼不使用計時器觸發器? 基於 CRON 的計時器觸發器會在固定時間運行,無論前一次執行是否結束。 永續協調流程會等候工作完成後,才排程下一次反覆項目,因此執行作業絕不會重疊。

Approach 排程 (間隔 1 小時,30 分鐘作業) 重疊風險
計時器觸發器(CRON) 1:00, 2:00, 3:00 是的——如果工作超過了時間範圍
永恆的管弦樂編排 1:00, 2:30, 4:00 否 — 下一次執行會等候完成
孤立工人模型
[Function("Periodic_Cleanup_Loop")]
public static async Task Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    await context.CallActivityAsync("DoCleanup");

    // sleep for one hour between cleanups
    DateTime nextCleanup = context.CurrentUtcDateTime.AddHours(1);
    await context.CreateTimer(nextCleanup, CancellationToken.None);

    context.ContinueAsNew(null);
}

進行中模型
[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 永續性客戶端方法來啟動持續編排,就像其他編排功能一樣。 為了確保一次只執行一個實例,請使用固定實例 ID。 欲了解更多資訊,請參見單調管配器。

孤立工人模型
[Function("Trigger_Eternal_Orchestration")]
public static async Task<HttpResponseData> OrchestrationTrigger(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData request,
    [DurableClient] DurableTaskClient client)
{
    string instanceId = "StaticId";

    await client.ScheduleNewOrchestrationInstanceAsync(
        "Periodic_Cleanup_Loop",
        null,
        new StartOrchestrationOptions { InstanceId = instanceId });
    return await client.CreateCheckStatusResponseAsync(request, instanceId);
}

進行中模型
[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.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");

進行中模型
await client.TerminateAsync(instanceId, "Cleanup no longer needed");

欲了解更多資訊,請參閱 實例管理

如果協調流程最終需要完成,請不要呼叫 continue-as-new,讓協調流程結束即可。

如果編排處於無限迴圈且需要停止,請使用持久任務用戶端的 終止 API 來停止它。

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

下一步