Durable Functions 模式和技术概念 (Azure Functions)Durable Functions patterns and technical concepts (Azure Functions)

Durable Functions 是 Azure FunctionsAzure WebJobs 的扩展。Durable Functions is an extension of Azure Functions and Azure WebJobs. 可以使用 Durable Functions 在无服务器环境中编写有状态函数。You can use Durable Functions to write stateful functions in a serverless environment. 该扩展可用于管理状态、检查点和重启。The extension manages state, checkpoints, and restarts for you.

本文提供有关 Azure Functions 的 Durable Functions 扩展的行为和常用实现模式的详细信息。This article gives you detailed information about the behaviors of the Durable Functions extension for Azure Functions and common implementation patterns. 这些信息可帮助你确定如何使用 Durable Functions 来解决开发难题。The information can help you determine how to use Durable Functions to help solve your development challenges.

Note

Durable Functions 是 Azure Functions 的高级扩展,并不适用于所有应用程序。Durable Functions is an advanced extension for Azure Functions that isn't appropriate for all applications. 本文假设读者非常熟悉 Azure Functions 中的概念,以及无服务器应用程序开发的难题。This article assumes that you have a strong familiarity with concepts in Azure Functions and the challenges involved in serverless application development.

模式Patterns

本部分介绍一些可以使用 Durable Functions 的常见应用程序模式。This section describes some common application patterns where Durable Functions can be useful.

模式 #1:函数链Pattern #1: Function chaining

在函数链模式中,会按特定的顺序执行一系列函数。In the function chaining pattern, a sequence of functions executes in a specific order. 在此模式中,一个函数的输出将应用到另一函数的输入。In this pattern, the output of one function is applied to the input of another function.

函数链模式的示意图

可以使用 Durable Functions 精简实现函数链模式,如以下示例所示:You can use Durable Functions to implement the function chaining pattern concisely as shown in the following example:

C# 脚本C# script

public static async Task<object> Run(DurableOrchestrationContext context)
{
    try
    {
        var x = await context.CallActivityAsync<object>("F1");
        var y = await context.CallActivityAsync<object>("F2", x);
        var z = await context.CallActivityAsync<object>("F3", y);
        return  await context.CallActivityAsync<object>("F4", z);
    }
    catch (Exception)
    {
        // Error handling or compensation goes here.
    }
}

Note

使用 C# 编写预编译的持久函数,与使用本示例中所示的 C# 脚本编写预编译的持久函数存在细微的差别。There are subtle differences between writing a precompiled durable function in C# and writing a precompiled durable function in the C# script that's shown in the example. 在 C# 预编译函数中,必须使用相应的属性来修饰持久参数。In a C# precompiled function, durable parameters must be decorated with respective attributes. 例如,使用 [OrchestrationTrigger] 属性修饰 DurableOrchestrationContext 参数。An example is the [OrchestrationTrigger] attribute for the DurableOrchestrationContext parameter. 在 C# 预编译持久函数中,如果未正确修饰参数,则运行时无法将变量注入该函数,并且会出现错误。In a C# precompiled durable function, if the parameters aren't properly decorated, the runtime can't inject the variables into the function, and an error occurs. 有关更多示例,请参阅 GitHub 上的 azure-functions-durable-extension 示例For more examples, see the azure-functions-durable-extension samples on GitHub.

JavaScript(仅限 Functions 2.x)JavaScript (Functions 2.x only)

const df = require("durable-functions");

module.exports = df.orchestrator(function*(context) {
    const x = yield context.df.callActivity("F1");
    const y = yield context.df.callActivity("F2", x);
    const z = yield context.df.callActivity("F3", y);
    return yield context.df.callActivity("F4", z);
});

在此示例中,值 F1F2F3F4 是函数应用中其他函数的名称。In this example, the values F1, F2, F3, and F4 are the names of other functions in the function app. 可以使用一般命令性编码构造来实现控制流。You can implement control flow by using normal imperative coding constructs. 代码按从上到下的顺序执行。Code executes from the top down. 代码可能涉及现有的语言控制流语义,例如条件语句和循环语句。The code can involve existing language control flow semantics, like conditionals and loops. 可在 try/catch/finally 块中包含错误处理逻辑。You can include error handling logic in try/catch/finally blocks.

可以使用 context 参数 DurableOrchestrationContext (.NET) 和 context.df 对象 (JavaScript) 按名称调用其他函数、传递参数并返回函数输出。You can use the context parameter DurableOrchestrationContext (.NET) and the context.df object (JavaScript) to invoke other functions by name, pass parameters, and return function output. 每当代码调用 await (C#) 或 yield (JavaScript) 时,Durable Functions 框架都会对当前函数实例的进度执行检查点操作。Each time the code calls await (C#) or yield (JavaScript), the Durable Functions framework checkpoints the progress of the current function instance. 如果在执行中途回收进程或 VM,则函数实例从上一个 awaityield 调用继续执行。If the process or VM recycles midway through the execution, the function instance resumes from the preceding await or yield call. 有关详细信息,请参阅下一部分“模式 #2:扇出/扇入”。For more information, see the next section, Pattern #2: Fan out/fan in.

Note

JavaScript 中的 context 对象表示整个函数上下文,而不仅仅表示 DurableOrchestrationContext 参数。The context object in JavaScript represents the entire function context, not only the DurableOrchestrationContext parameter.

模式 #2:扇出/扇入Pattern #2: Fan out/fan in

在扇出/扇入模式中,可以并行执行多个函数,然后等待所有函数完成。In the fan out/fan in pattern, you execute multiple functions in parallel and then wait for all functions to finish. 通常会对这些函数返回的结果执行一些聚合操作。Often, some aggregation work is done on the results that are returned from the functions.

扇出/扇入模式的示意图

对于一般函数,可通过使函数向某个队列发送多条消息来完成扇出。With normal functions, you can fan out by having the function send multiple messages to a queue. 扇入回来的难度要大得多。Fanning back in is much more challenging. 若要扇入,可在一般函数中编写代码,以跟踪队列触发的函数何时结束,然后存储函数输出。To fan in, in a normal function, you write code to track when the queue-triggered functions end, and then store function outputs.

Durable Functions 扩展使用相对简单的代码处理此模式:The Durable Functions extension handles this pattern with relatively simple code:

C# 脚本C# script

public static async Task Run(DurableOrchestrationContext context)
{
    var parallelTasks = new List<Task<int>>();

    // Get a list of N work items to process in parallel.
    object[] workBatch = await context.CallActivityAsync<object[]>("F1");
    for (int i = 0; i < workBatch.Length; i++)
    {
        Task<int> task = context.CallActivityAsync<int>("F2", workBatch[i]);
        parallelTasks.Add(task);
    }

    await Task.WhenAll(parallelTasks);

    // Aggregate all N outputs and send the result to F3.
    int sum = parallelTasks.Sum(t => t.Result);
    await context.CallActivityAsync("F3", sum);
}

JavaScript(仅限 Functions 2.x)JavaScript (Functions 2.x only)

const df = require("durable-functions");

module.exports = df.orchestrator(function*(context) {
    const parallelTasks = [];

    // Get a list of N work items to process in parallel.
    const workBatch = yield context.df.callActivity("F1");
    for (let i = 0; i < workBatch.length; i++) {
        parallelTasks.push(context.df.callActivity("F2", workBatch[i]));
    }

    yield context.df.Task.all(parallelTasks);

    // Aggregate all N outputs and send the result to F3.
    const sum = parallelTasks.reduce((prev, curr) => prev + curr, 0);
    yield context.df.callActivity("F3", sum);
});

扇出操作将分散到 F2 函数的多个实例。The fan-out work is distributed to multiple instances of the F2 function. 使用动态任务列表跟踪这些操作。The work is tracked by using a dynamic list of tasks. 调用 .NET Task.WhenAll API 或 JavaScript context.df.Task.all API 等待所有调用的函数完成。The .NET Task.WhenAll API or JavaScript context.df.Task.all API is called, to wait for all the called functions to finish. 然后,从动态任务列表聚合 F2 函数输出,并将这些输出传递给 F3 函数。Then, the F2 function outputs are aggregated from the dynamic task list and passed to the F3 function.

在针对 Task.WhenAllcontext.df.Task.all 调用 awaityield 时自动执行的检查点操作确保中途可能出现的任何崩溃或重新启动无需重启已完成的任务。The automatic checkpointing that happens at the await or yield call on Task.WhenAll or context.df.Task.all ensures that a potential midway crash or reboot doesn't require restarting an already completed task.

模式 #3:异步 HTTP APIPattern #3: Async HTTP APIs

异步 HTTP API 模式可解决使用外部客户端协调长时间运行的操作状态时出现的问题。The async HTTP APIs pattern addresses the problem of coordinating the state of long-running operations with external clients. 实现此模式的一种常用方式是让 HTTP 调用触发长时间运行的操作。A common way to implement this pattern is by having an HTTP call trigger the long-running action. 然后,将客户端重定向到某个状态终结点,客户端可轮询该终结点,以了解操作是何时完成的。Then, redirect the client to a status endpoint that the client polls to learn when the operation is finished.

HTTP API 模式的示意图

Durable Functions 提供内置 API,用于简化为与长时间运行的函数执行进行交互而编写的代码。Durable Functions provides built-in APIs that simplify the code you write to interact with long-running function executions. Durable Functions 快速入门示例(C#JavaScript)演示了可用于启动新业务流程协调程序函数实例的简单 REST 命令。The Durable Functions quickstart samples (C# and JavaScript) show a simple REST command that you can use to start new orchestrator function instances. 启动实例后,该扩展会公开 Webhook HTTP API 用于查询业务流程协调程序函数的状态。After an instance starts, the extension exposes webhook HTTP APIs that query the orchestrator function status.

以下示例演示用于启动业务流程协调程序和查询其状态的 REST 命令。The following example shows REST commands that start an orchestrator and query its status. 为清楚起见,实例中省略了某些细节。For clarity, some details are omitted from the example.

> curl -X POST https://myfunc.chinacloudsites.cn/orchestrators/DoWork -H "Content-Length: 0" -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.chinacloudsites.cn/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec

{"id":"b79baf67f717453ca9e86c5da21e03ec", ...}

> curl https://myfunc.chinacloudsites.cn/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.chinacloudsites.cn/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec

{"runtimeStatus":"Running","lastUpdatedTime":"2017-03-16T21:20:47Z", ...}

> curl https://myfunc.chinacloudsites.cn/admin/extensions/DurableTaskExtension/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 200 OK
Content-Length: 175
Content-Type: application/json

{"runtimeStatus":"Completed","lastUpdatedTime":"2017-03-16T21:20:57Z", ...}

因为 Durable Functions 运行时会管理状态,因此你无需实现自己的状态跟踪机制。Because the Durable Functions runtime manages state, you don't need to implement your own status-tracking mechanism.

Durable Functions 扩展提供内置的 Webhook 用于管理长时间运行的业务流程。The Durable Functions extension has built-in webhooks that manage long-running orchestrations. 你可以使用自己的函数触发器(例如 HTTP、队列或 Azure 事件中心)和 orchestrationClient 绑定来自行实现此模式。You can implement this pattern yourself by using your own function triggers (such as HTTP, a queue, or Azure Event Hubs) and the orchestrationClient binding. 例如,可以使用队列消息触发终止。For example, you might use a queue message to trigger termination. 或者,可以使用受 Azure Active Directory 身份验证策略保护的 HTTP 触发器,而不是使用利用生成的密钥进行身份验证的内置 Webhook。Or, you might use an HTTP trigger that's protected by an Azure Active Directory authentication policy instead of the built-in webhooks that use a generated key for authentication.

以下示例演示如何使用 HTTP API 模式:Here are some examples of how to use the HTTP API pattern:

C#C#

// An HTTP-triggered function starts a new orchestrator function instance.
public static async Task<HttpResponseMessage> Run(
    HttpRequestMessage req,
    DurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{
    // The function name comes from the request URL.
    // The function input comes from the request content.
    dynamic eventData = await req.Content.ReadAsAsync<object>();
    string instanceId = await starter.StartNewAsync(functionName, eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);
}

JavaScript(仅限 Functions 2.x)JavaScript (Functions 2.x only)

// An HTTP-triggered function starts a new orchestrator function instance.
const df = require("durable-functions");

module.exports = async function (context, req) {
    const client = df.getClient(context);

    // The function name comes from the request URL.
    // The function input comes from the request content.
    const eventData = req.body;
    const instanceId = await client.startNew(req.params.functionName, undefined, eventData);

    context.log(`Started orchestration with ID = '${instanceId}'.`);

    return client.createCheckStatusResponse(req, instanceId);
};

Warning

在 JavaScript 中进行本地开发时,若要使用 DurableOrchestrationClient 中的方法,必须将环境变量 WEBSITE_HOSTNAME 设置为 localhost:<port>(例如 localhost:7071)。When you develop locally in JavaScript, to use methods on DurableOrchestrationClient, you must set the environment variable WEBSITE_HOSTNAME to localhost:<port> (for example, localhost:7071). 有关此项要求的详细信息,请参阅 GitHub 问题 28For more information about this requirement, see GitHub issue 28.

在 .NET 中,DurableOrchestrationClient starter 参数是 orchestrationClient 输出绑定中的值,该绑定是 Durable Functions 扩展的一部分。In .NET, the DurableOrchestrationClient starter parameter is a value from the orchestrationClient output binding, which is part of the Durable Functions extension. 在 JavaScript 中,此对象是通过调用 df.getClient(context) 返回的。In JavaScript, this object is returned by calling df.getClient(context). 这些对象提供了可用于启动、向其发送事件、终止和查询新的或现有的业务流程协调程序函数实例的方法。These objects provide methods you can use to start, send events to, terminate, and query for new or existing orchestrator function instances.

在前面的示例中,HTTP 触发的函数采用传入的 URL 中的 functionName 值,并将该值传递给 StartNewAsyncIn the preceding examples, an HTTP-triggered function takes in a functionName value from the incoming URL and passes the value to StartNewAsync. 然后,CreateCheckStatusResponse 绑定 API 返回包含 Location 标头和有关实例的其他信息的响应。The CreateCheckStatusResponse binding API then returns a response that contains a Location header and additional information about the instance. 稍后可以使用这些信息来查找已启动实例的状态或终止实例。You can use the information later to look up the status of the started instance or to terminate the instance.

模式 #4:监视Pattern #4: Monitor

监视模式是指工作流中某个灵活的重复性过程。The monitor pattern refers to a flexible, recurring process in a workflow. 例如,不断轮询,直到满足特定的条件为止。An example is polling until specific conditions are met. 可以使用常规计时器触发器解决简单方案(例如定期清理作业),但该方案的间隔是静态的,并且管理实例生存期会变得复杂。You can use a regular timer trigger to address a basic scenario, such as a periodic cleanup job, but its interval is static and managing instance lifetimes becomes complex. 可以使用 Durable Functions 创建灵活的重复间隔、管理任务生存期,以及从单个业务流程创建多个监视过程。You can use Durable Functions to create flexible recurrence intervals, manage task lifetimes, and create multiple monitor processes from a single orchestration.

监视模式的一个例子是反转前面所述的异步 HTTP API 方案。An example of the monitor pattern is to reverse the earlier async HTTP API scenario. 监视模式不会公开终结点供外部客户端监视长时间运行的操作,而是让长时间运行的监视器使用外部终结点,然后等待某个状态发生更改。Instead of exposing an endpoint for an external client to monitor a long-running operation, the long-running monitor consumes an external endpoint, and then waits for a state change.

监视模式的示意图

只需编写少量的代码行,即可使用 Durable Functions 创建多个监视器来观察任意终结点。In a few lines of code, you can use Durable Functions to create multiple monitors that observe arbitrary endpoints. 满足某个条件时,监视器可以结束执行;或者,DurableOrchestrationClient 可以终止监视器。The monitors can end execution when a condition is met, or the DurableOrchestrationClient can terminate the monitors. 可以根据特定的条件(例如指数退避)更改监视器的 wait 间隔。You can change a monitor's wait interval based on a specific condition (for example, exponential backoff.)

以下代码实现一个基本的监视器:The following code implements a basic monitor:

C# 脚本C# script

public static async Task Run(DurableOrchestrationContext context)
{
    int jobId = context.GetInput<int>();
    int pollingInterval = GetPollingInterval();
    DateTime expiryTime = GetExpiryTime();

    while (context.CurrentUtcDateTime < expiryTime)
    {
        var jobStatus = await context.CallActivityAsync<string>("GetJobStatus", jobId);
        if (jobStatus == "Completed")
        {
            // Perform an action when a condition is met.
            await context.CallActivityAsync("SendAlert", machineId);
            break;
        }

        // Orchestration sleeps until this time.
        var nextCheck = context.CurrentUtcDateTime.AddSeconds(pollingInterval);
        await context.CreateTimer(nextCheck, CancellationToken.None);
    }

    // Perform more work here, or let the orchestration end.
}

JavaScript(仅限 Functions 2.x)JavaScript (Functions 2.x only)

const df = require("durable-functions");
const moment = require("moment");

module.exports = df.orchestrator(function*(context) {
    const jobId = context.df.getInput();
    const pollingInternal = getPollingInterval();
    const expiryTime = getExpiryTime();

    while (moment.utc(context.df.currentUtcDateTime).isBefore(expiryTime)) {
        const jobStatus = yield context.df.callActivity("GetJobStatus", jobId);
        if (jobStatus === "Completed") {
            // Perform an action when a condition is met.
            yield context.df.callActivity("SendAlert", machineId);
            break;
        }

        // Orchestration sleeps until this time.
        const nextCheck = moment.utc(context.df.currentUtcDateTime).add(pollingInterval, 's');
        yield context.df.createTimer(nextCheck.toDate());
    }

    // Perform more work here, or let the orchestration end.
});

收到请求时,会为该作业 ID 创建新的业务流程实例。When a request is received, a new orchestration instance is created for that job ID. 该实例会一直轮询状态,直到满足条件退出循环。The instance polls a status until a condition is met and the loop is exited. 持久计时器控制轮询间隔。A durable timer controls the polling interval. 然后可以执行其他操作,或者可以结束业务流程。Then, more work can be performed, or the orchestration can end. context.CurrentUtcDateTime (.NET) 或 context.df.currentUtcDateTime (JavaScript) 超出 expiryTime 值时,监视器将会终止。When the context.CurrentUtcDateTime (.NET) or context.df.currentUtcDateTime (JavaScript) exceeds the expiryTime value, the monitor ends.

模式 #5:人机交互Pattern #5: Human interaction

许多自动化过程涉及到某种人机交互。Many automated processes involve some kind of human interaction. 自动化过程中涉及的人机交互非常棘手,因为人的可用性和响应能力不如云服务那样高。Involving humans in an automated process is tricky because people aren't as highly available and as responsive as cloud services. 自动化过程可以使用超时和补偿逻辑来实现此目的。An automated process might allow for this by using timeouts and compensation logic.

审批过程就是涉及到人机交互的业务过程的一个例子。An approval process is an example of a business process that involves human interaction. 例如,某份超出特定金额的开支报表需要经理的审批。Approval from a manager might be required for an expense report that exceeds a certain amount. 如果经理未在 72 小时内审批该开支报表(经理可能正在度假),则会启动上报过程,让其他某人(可能是经理的经理)审批。If the manager doesn't approve the expense report within 72 hours (maybe the manager went on vacation), an escalation process kicks in to get the approval from someone else (perhaps the manager's manager).

人机交互模式的示意图

在此示例中,可以使用业务流程协调程序函数实现该模式。You can implement the pattern in this example by using an orchestrator function. 业务流程协调程序使用持久计时器请求审批。The orchestrator uses a durable timer to request approval. 如果发生超时,业务流程协调程序会将事务上报。The orchestrator escalates if timeout occurs. 业务流程协调程序等待发生某个外部事件,例如,人机交互生成的通知。The orchestrator waits for an external event, such as a notification that's generated by a human interaction.

这些示例创建一个审批过程来演示人机交互模式:These examples create an approval process to demonstrate the human interaction pattern:

C# 脚本C# script

public static async Task Run(DurableOrchestrationContext context)
{
    await context.CallActivityAsync("RequestApproval");
    using (var timeoutCts = new CancellationTokenSource())
    {
        DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
        Task durableTimeout = context.CreateTimer(dueTime, timeoutCts.Token);

        Task<bool> approvalEvent = context.WaitForExternalEvent<bool>("ApprovalEvent");
        if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout))
        {
            timeoutCts.Cancel();
            await context.CallActivityAsync("ProcessApproval", approvalEvent.Result);
        }
        else
        {
            await context.CallActivityAsync("Escalate");
        }
    }
}

JavaScript(仅限 Functions 2.x)JavaScript (Functions 2.x only)

const df = require("durable-functions");
const moment = require('moment');

module.exports = df.orchestrator(function*(context) {
    yield context.df.callActivity("RequestApproval");

    const dueTime = moment.utc(context.df.currentUtcDateTime).add(72, 'h');
    const durableTimeout = context.df.createTimer(dueTime.toDate());

    const approvalEvent = context.df.waitForExternalEvent("ApprovalEvent");
    if (approvalEvent === yield context.df.Task.any([approvalEvent, durableTimeout])) {
        durableTimeout.cancel();
        yield context.df.callActivity("ProcessApproval", approvalEvent.result);
    } else {
        yield context.df.callActivity("Escalate");
    }
});

若要创建持久计时器,请调用 context.CreateTimer (.NET) 或 context.df.createTimer (JavaScript)。To create the durable timer, call context.CreateTimer (.NET) or context.df.createTimer (JavaScript). 通知由 context.WaitForExternalEvent (.NET) 或 context.df.waitForExternalEvent (JavaScript) 接收。The notification is received by context.WaitForExternalEvent (.NET) or context.df.waitForExternalEvent (JavaScript). 然后,调用 Task.WhenAny (.NET) 或 context.df.Task.any (JavaScript) 来确定是上报(首先发生超时)还是处理审批(超时前收到审批)。Then, Task.WhenAny (.NET) or context.df.Task.any (JavaScript) is called to decide whether to escalate (timeout happens first) or process the approval (the approval is received before timeout).

外部客户端可以使用内置 HTTP API 或通过另一个函数使用 DurableOrchestrationClient.RaiseEventAsync API 将事件通知传递给正在等待的业务流程协调程序函数:An external client can deliver the event notification to a waiting orchestrator function by using either the built-in HTTP APIs or by using the DurableOrchestrationClient.RaiseEventAsync API from another function:

public static async Task Run(string instanceId, DurableOrchestrationClient client)
{
    bool isApproved = true;
    await client.RaiseEventAsync(instanceId, "ApprovalEvent", isApproved);
}
const df = require("durable-functions");

module.exports = async function (context) {
    const client = df.getClient(context);
    const isApproved = true;
    await client.raiseEvent(instanceId, "ApprovalEvent", isApproved);
};

技术The technology

在幕后,Durable Functions 扩展构建在 Durable Task Framework(GitHub 上的用于生成持久任务业务流程的开源库)的基础之上。Behind the scenes, the Durable Functions extension is built on top of the Durable Task Framework, an open-source library on GitHub that's used to build durable task orchestrations. 如同 Azure Functions 是 Azure WebJobs 的无服务器演进一样,Durable Functions 是 Durable Task Framework 的无服务器演进。Like Azure Functions is the serverless evolution of Azure WebJobs, Durable Functions is the serverless evolution of the Durable Task Framework. Microsoft 和其他组织广泛使用 Durable Task Framework 来自动处理任务关键型过程。Microsoft and other organizations use the Durable Task Framework extensively to automate mission-critical processes. 它天生就很适合无服务器 Azure Functions 环境。It's a natural fit for the serverless Azure Functions environment.

事件溯源、检查点和重播Event sourcing, checkpointing, and replay

业务流程协调程序函数使用事件溯源设计模式可靠维护其执行状态。Orchestrator functions reliably maintain their execution state by using the event sourcing design pattern. Durable Functions 扩展使用仅限追加的存储来记录函数业务流程执行的一系列完整操作,而不是直接存储业务流程的当前状态。Instead of directly storing the current state of an orchestration, the Durable Functions extension uses an append-only store to record the full series of actions the function orchestration takes. 与“转储”完整的运行时状态相比,仅限追加的存储具有诸多优势。An append-only store has many benefits compared to "dumping" the full runtime state. 优势包括提升性能、可伸缩性和响应能力。Benefits include increased performance, scalability, and responsiveness. 此外,还可以确保事务数据的最终一致性,保持完整的审核线索和历史记录。You also get eventual consistency for transactional data and full audit trails and history. 审核线索支持可靠的补偿操作。The audit trails support reliable compensating actions.

Durable Functions 以透明方式使用事件溯源。Durable Functions uses event sourcing transparently. 在幕后,业务流程协调程序函数中的 await (C#) 或 yield (JavaScript) 运算符将对业务流程协调程序线程的控制权让回给 Durable Task Framework 调度程序。Behind the scenes, the await (C#) or yield (JavaScript) operator in an orchestrator function yields control of the orchestrator thread back to the Durable Task Framework dispatcher. 然后,该调度程序向存储提交业务流程协调程序函数计划的任何新操作(如调用一个或多个子函数或计划持久计时器)。The dispatcher then commits any new actions that the orchestrator function scheduled (such as calling one or more child functions or scheduling a durable timer) to storage. 透明的提交操作会追加到业务流程实例的执行历史记录中。The transparent commit action appends to the execution history of the orchestration instance. 历史记录存储在存储表中。The history is stored in a storage table. 然后,提交操作向队列添加消息,以计划实际工作。The commit action then adds messages to a queue to schedule the actual work. 此时,可从内存中卸载业务流程协调程序函数。At this point, the orchestrator function can be unloaded from memory.

如果正在使用 Azure Functions 消耗计划,将停止对业务流程协调程序函数的计费。Billing for the orchestrator function stops if you're using the Azure Functions consumption plan. 如果需要完成其他工作,可重启该函数并重新构造其状态。When there's more work to do, the function restarts, and its state is reconstructed.

如果业务流程函数需要执行其他工作(例如,收到响应消息或持久计时器过期),业务流程协调程序将唤醒并从头开始重新执行整个函数,以重新生成本地状态。When an orchestration function is given more work to do (for example, a response message is received or a durable timer expires), the orchestrator wakes up and re-executes the entire function from the start to rebuild the local state.

在重放期间,如果代码尝试调用某个函数(或执行任何其他异步工作),Durable Task Framework 会查询当前业务流程的执行历史记录。During the replay, if the code tries to call a function (or do any other async work), the Durable Task Framework consults the execution history of the current orchestration. 如果该扩展发现活动函数已执行并已生成某种结果,则会重放该函数的结果,并且业务流程协调程序代码会继续运行。If it finds that the activity function has already executed and yielded a result, it replays that function's result and the orchestrator code continues to run. 在函数代码完成或计划了新的异步工作之前,重放会一直继续。Replay continues until the function code is finished or until it has scheduled new async work.

业务流程协调程序代码约束Orchestrator code constraints

业务流程协调程序代码的重放行为针对可在业务流程协调程序函数中编写的代码类型创建约束。The replay behavior of orchestrator code creates constraints on the type of code that you can write in an orchestrator function. 例如,业务流程协调程序代码必须具有确定性,因为该代码将重放多次,每次必须生成相同的结果。For example, orchestrator code must be deterministic because it will be replayed multiple times, and it must produce the same result each time. 有关约束的完整列表,请参阅业务流程协调程序代码约束For the complete list of constraints, see Orchestrator code constraints.

存储和可伸缩性Storage and scalability

Durable Functions 扩展使用 Azure 存储中的队列、表和 Blob 来持久保存执行历史记录状态和触发函数执行。The Durable Functions extension uses queues, tables, and blobs in Azure Storage to persist execution history state and trigger function execution. 可以使用函数应用的默认存储帐户,也可以配置单独的存储帐户。You can use the default storage account for the function app, or you can configure a separate storage account. 由于存储吞吐量存在限制,你可能需要配置单独的帐户。You might want a separate account based on storage throughput limits. 编写的业务流程协调程序代码不会与这些存储帐户中的实体进行交互。The orchestrator code you write doesn't interact with the entities in these storage accounts. Durable Task Framework 直接将实体作为实现详细信息进行管理。The Durable Task Framework manages the entities directly as an implementation detail.

业务流程协调程序函数通过内部队列消息计划活动函数和接收这些函数的响应。Orchestrator functions schedule activity functions and receive their responses via internal queue messages. 如果在 Azure Functions 消耗计划中运行函数应用,则 Azure Functions 缩放控制器会监视这些队列。When a function app runs in the Azure Functions consumption plan, the Azure Functions scale controller monitors these queues. 将会根据需要添加新的计算实例。New compute instances are added as needed. 横向扩展到多个 VM 后,业务流程协调程序函数可在一个 VM 上运行,它调用的活动函数可在多个不同的 VM 上运行。When scaled out to multiple VMs, an orchestrator function might run on one VM, while activity functions that the orchestrator function calls might run on several different VMs. 有关 Durable Functions 的缩放行为的详细信息,请参阅性能和缩放For more information about the scale behavior of Durable Functions, see Performance and scale.

业务流程协调程序帐户的执行历史记录存储在表存储中。The execution history for orchestrator accounts is stored in table storage. 每当某个实例在特定的 VM 上解冻时,业务流程协调程序会从表存储中获取该实例的执行历史记录,以便可以重新生成其本地状态。Whenever an instance rehydrates on a particular VM, the orchestrator fetches its execution history from table storage so it can rebuild its local state. 在表存储中获取历史记录所带来的一项便利是,可以使用 Azure 存储资源管理器等工具查看业务流程的历史记录。A convenient aspect of having the history available in table storage is that you can use tools like Azure Storage Explorer to see the history of your orchestrations.

存储 Blob 主要用作一种租用机制,用于协调跨多个 VM 的业务流程实例的横向扩展。Storage blobs are primarily used as a leasing mechanism to coordinate the scale-out of orchestration instances across multiple VMs. 存储 Blob 可以保存无法直接存储在表或队列中的大型消息的数据。Storage blobs hold data for large messages that can't be stored directly in tables or queues.

Azure 存储资源管理器的屏幕截图

Warning

尽管可以在表存储中轻松查看执行历史记录,但请不要对此表有任何依赖。Although it's easy and convenient to see execution history in table storage, don't make any dependencies on this table. 该表可能会随着 Durable Functions 扩展的演变而发生变化。The table might change as the Durable Functions extension evolves.

已知问题Known issues

应在 GitHub 问题列表中跟踪所有已知问题。All known issues should be tracked in the GitHub issues list. 如果遇到 GitHub 中未列出的问题,请提出新的问题。If you run into a problem and can't find the issue in GitHub, open a new issue. 请详细描述问题。Include a detailed description of the problem.

后续步骤Next steps

若要详细了解 Durable Functions,请参阅 Durable Functions 函数类型和功能To learn more about Durable Functions, see Durable Functions function types and features.

开始操作:To get started: