Muster und technische Konzepte von Durable Functions (Azure Functions)Durable Functions patterns and technical concepts (Azure Functions)

Durable Functions ist eine Erweiterung von Azure Functions und Azure WebJobs.Durable Functions is an extension of Azure Functions and Azure WebJobs. Mithilfe von Durable Functions können Sie zustandsbehaftete Funktionen in einer serverlosen Umgebung schreiben.You can use Durable Functions to write stateful functions in a serverless environment. Die Erweiterung verwaltet Status, Prüfpunkte und Neustarts für Sie.The extension manages state, checkpoints, and restarts for you.

Dieser Artikel bietet Ihnen ausführliche Informationen über das Verhalten der Erweiterung Durable Functions für Azure Functions und häufige Implementierungsmuster.This article gives you detailed information about the behaviors of the Durable Functions extension for Azure Functions and common implementation patterns. Diese Informationen unterstützen Sie dabei, zu bestimmen, wie Sie Durable Functions zur Lösung von Entwicklungsproblemen verwenden können.The information can help you determine how to use Durable Functions to help solve your development challenges.

Hinweis

Durable Functions ist eine fortgeschrittene Erweiterung für Azure Functions und nicht für alle Anwendungen geeignet.Durable Functions is an advanced extension for Azure Functions that isn't appropriate for all applications. In diesem Artikel wird vorausgesetzt, dass Sie mit den Konzepten von Azure Functions und den Herausforderungen serverloser Anwendungsentwicklung bestens vertraut sind.This article assumes that you have a strong familiarity with concepts in Azure Functions and the challenges involved in serverless application development.

MusterPatterns

In diesem Abschnitt werden einige typische Anwendungsmuster beschrieben, die von Durable Functions profitieren können.This section describes some common application patterns where Durable Functions can be useful.

Muster 1: FunktionsverkettungPattern #1: Function chaining

Beim Muster der Funktionsverkettung wird eine Abfolge von Funktionen in einer bestimmten Reihenfolge ausgeführt.In the function chaining pattern, a sequence of functions executes in a specific order. Bei diesem Muster wird die Ausgabe einer Funktion als Eingabe einer weiteren Funktion verwendet.In this pattern, the output of one function is applied to the input of another function.

Ein Diagramm des Funktionsverkettungsmusters

Mithilfe von Durable Functions können Sie das Funktionsverkettungsmuster präzise wie im folgenden Beispiel dargestellt implementieren:You can use Durable Functions to implement the function chaining pattern concisely as shown in the following example:

C#-SkriptC# 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.
    }
}

Hinweis

Es gibt feine Unterschiede zwischen dem Schreiben einer vorkompilierten permanenten Funktion in C# und dem Schreiben einer vorkompilierten permanenten Funktion in dem C#-Skript, das in diesem Beispiel gezeigt wird.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. In einer vorkompilierten C#-Funktion müssen permanente Parameter mit den jeweiligen Attributen versehen werden.In a C# precompiled function, durable parameters must be decorated with respective attributes. Ein Beispiel ist das [OrchestrationTrigger]-Attribut für den DurableOrchestrationContext-Parameter.An example is the [OrchestrationTrigger] attribute for the DurableOrchestrationContext parameter. Wenn in einer vorkompilierten permanenten C#-Funktion die Parameter nicht ordnungsgemäß mit Attributen versehen sind, kann die Runtime die Variablen nicht in die Funktion einfügen, und ein Fehler tritt auf.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. Weitere Beispiele finden Sie in den Azure-Functions-Durable-Extension-Beispielen auf GitHub.For more examples, see the azure-functions-durable-extension samples on GitHub.

JavaScript (nur 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);
});

In diesem Beispiel sind die Werte F1, F2, F3 und F4 die Namen weiterer Funktionen in der Funktions-App.In this example, the values F1, F2, F3, and F4 are the names of other functions in the function app. Sie können die Ablaufsteuerung mithilfe normaler imperativer Codierungskonstrukte implementieren.You can implement control flow by using normal imperative coding constructs. Der Code wird von oben nach unten ausgeführt.Code executes from the top down. Der Code kann bestehende sprachliche Ablaufsteuerungssemantik wie Bedingungsanweisungen und Schleifen umfassen.The code can involve existing language control flow semantics, like conditionals and loops. Sie können Logik zur Fehlerbehandlung in try/catch/finally-Blöcken einschließen.You can include error handling logic in try/catch/finally blocks.

Sie können den context-Parameter DurableOrchestrationContext (.NET) und das context.df-Objekt (JavaScript) verwenden, um andere Funktionen anhand des Namens aufzurufen, Parameter zu übergeben und Funktionsausgaben zurückzugeben.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. Jedes Mal, wenn der Code await (C#) oder yield (JavaScript) aufruft, erstellt das Durable Functions Framework Prüfpunkte zum Status der aktuellen Funktionsinstanz.Each time the code calls await (C#) or yield (JavaScript), the Durable Functions framework checkpoints the progress of the current function instance. Wenn der Prozess oder die VM mitten in der Ausführung neu gestartet wird, wird die Funktionsinstanz ab dem vorherigen await- bzw. yield-Aufruf fortgesetzt.If the process or VM recycles midway through the execution, the function instance resumes from the preceding await or yield call. Weitere Informationen finden Sie im nächsten Abschnitt, Muster 2: Auffächern auswärts/einwärts.For more information, see the next section, Pattern #2: Fan out/fan in.

Hinweis

Das context-Objekt in JavaScript stellt gesamten den Funktionskontext dar, nicht nur den DurableOrchestrationContext-Parameter.The context object in JavaScript represents the entire function context, not only the DurableOrchestrationContext parameter.

Muster 2: Auffächern auswärts/einwärtsPattern #2: Fan out/fan in

Beim Muster Auffächern auswärts/einwärts werden mehrere Funktionen parallel ausgeführt und anschließend auf den Abschluss aller gewartet.In the fan out/fan in pattern, you execute multiple functions in parallel and then wait for all functions to finish. Häufig werden die von den Funktionen zurückgegebenen Ergebnisse einer Aggregation unterzogen.Often, some aggregation work is done on the results that are returned from the functions.

Ein Diagramm des Musters „Auffächern auswärts/einwärts“

Bei normalen Funktionen kann das Auffächern auswärts erfolgen, indem die Funktion mehrere Nachrichten an eine Warteschlange sendet.With normal functions, you can fan out by having the function send multiple messages to a queue. Das Auffächern zurück nach innen ist wesentlich schwieriger.Fanning back in is much more challenging. Für das Auffächern nach innen schreiben Sie in einer normalen Funktion Code, der nachverfolgt, wann die von der Warteschlange ausgelösten Funktionen enden und speichern dann ihre Ausgaben.To fan in, in a normal function, you write code to track when the queue-triggered functions end, and then store function outputs.

Die Erweiterung Durable Functions wird diesem Muster mit relativ einfachem Code gerecht:The Durable Functions extension handles this pattern with relatively simple code:

C#-SkriptC# 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 (nur 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);
});

Das Auffächern nach außen ist auf mehrere Instanzen der F2-Funktion verteilt.The fan-out work is distributed to multiple instances of the F2 function. Die Arbeit wird mithilfe einer dynamischen Aufgabenliste nachverfolgt.The work is tracked by using a dynamic list of tasks. Die .NET-API Task.WhenAll oder die JavaScript-API context.df.Task.all wird aufgerufen, um auf den Abschluss der aufgerufenen Funktionen zu warten.The .NET Task.WhenAll API or JavaScript context.df.Task.all API is called, to wait for all the called functions to finish. Anschließend werden die Ausgaben der F2-Funktion aus der dynamischen Aufgabenliste aggregiert und an die F3-Funktion übergeben.Then, the F2 function outputs are aggregated from the dynamic task list and passed to the F3 function.

Die automatische Prüfpunkterstellung, die beim await- oder yield-Aufruf für Task.WhenAll bzw. context.df.Task.all erfolgt, stellt sicher, dass ein potentieller Absturz oder Neustart während der Ausführung keinen Neustart bereits abgeschlossener Aufgaben erfordert.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.

Muster 3: Asynchrone HTTP-APIsPattern #3: Async HTTP APIs

Das Muster „asynchrone HTTP-APIs“ eignet sich für das Problem, den Status von Vorgängen mit langer Ausführungsdauer mit externen Clients zu koordinieren.The async HTTP APIs pattern addresses the problem of coordinating the state of long-running operations with external clients. Ein gängiges Verfahren zum Implementieren dieses Musters besteht darin, die Vorgänge mit langer Ausführungsdauer von einem HTTP-Aufruf auslösen zu lassen.A common way to implement this pattern is by having an HTTP call trigger the long-running action. Leiten Sie dann den Client zu einem Statusendpunkt um, den der Client abfragt, um herauszufinden, wenn der Vorgang abgeschlossen ist.Then, redirect the client to a status endpoint that the client polls to learn when the operation is finished.

Ein Diagramm des HTTP-API-Musters

Durable Functions bietet integrierte APIs, die den Code vereinfachen, den Sie für die Interaktion mit langen Funktionsausführungen schreiben.Durable Functions provides built-in APIs that simplify the code you write to interact with long-running function executions. Die Schnellstartbeispiele für Durable Functions (C# und JavaScript) zeigen einen einfachen REST-Befehl, den Sie zum Starten neuer Orchestratorfunktionsinstanzen verwenden können.The Durable Functions quickstart samples (C# and JavaScript) show a simple REST command that you can use to start new orchestrator function instances. Nach dem Start einer Instanz macht die Erweiterung Webhook-HTTP-APIs verfügbar, die den Status der Orchestratorfunktion abfragen.After an instance starts, the extension exposes webhook HTTP APIs that query the orchestrator function status.

Das folgende Beispiel zeigt REST-Befehle zum Starten eines Orchestrators und zum Abfragen seines Status.The following example shows REST commands that start an orchestrator and query its status. Zur besseren Übersichtlichkeit werden im Beispiel einige Details weggelassen.For clarity, some details are omitted from the example.

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

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

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

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

> curl https://myfunc.azurewebsites.net/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", ...}

Da der Status von der Durable Functions-Laufzeit verwaltet wird, müssen Sie keinen eigenen Mechanismus zur Statusnachverfolgung implementieren.Because the Durable Functions runtime manages state, you don't need to implement your own status-tracking mechanism.

Die Durable Functions-Erweiterung verfügt über integrierte Webhooks, die Orchestrierungen mit langer Ausführungsdauer verwalten.The Durable Functions extension has built-in webhooks that manage long-running orchestrations. Sie können dieses Muster selbst implementieren, indem Sie Ihre eigenen Funktionstrigger (wie etwa HTTP, eine Warteschlange oder Azure Event Hubs) und die orchestrationClient-Bindung verwenden.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. Sie können beispielsweise eine Warteschlangennachricht verwenden, um die Beendigung auszulösen.For example, you might use a queue message to trigger termination. Sie könnten auch anstelle der integrierten Webhooks, die einen generierten Schlüssel für die Authentifizierung verwenden, einen HTTP-Trigger verwenden, der durch eine Azure Active Directory-Authentifizierungsrichtlinie geschützt ist.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.

Hier finden Sie einige Beispiele zur Verwendung des HTTP-API-Musters: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 (nur 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);
};

In .NET ist der starter-Parameter von DurableOrchestrationClient ein Wert aus der orchestrationClient-Ausgabebindung, die Teil der Durable Functions-Erweiterung ist.In .NET, the DurableOrchestrationClient starter parameter is a value from the orchestrationClient output binding, which is part of the Durable Functions extension. In JavaScript wird dieses Objekt durch Aufrufen von df.getClient(context) zurückgegeben.In JavaScript, this object is returned by calling df.getClient(context). Diese Objekte stellen Methoden bereit, mit denen neue oder vorhandene Orchestratorfunktionsinstanzen gestartet, beendet und abgefragt sowie Ereignisse an sie gesendet werden können.These objects provide methods you can use to start, send events to, terminate, and query for new or existing orchestrator function instances.

In den vorherigen Beispielen nimmt eine über HTTP ausgelöste Funktion einen functionName-Wert von der eingehenden URL entgegen und übergibt den Wert an StartNewAsync.In the preceding examples, an HTTP-triggered function takes in a functionName value from the incoming URL and passes the value to StartNewAsync. Die Bindungs-API CreateCheckStatusResponse gibt dann eine Antwort zurück, die einen Location-Header und zusätzliche Informationen zur Instanz enthält.The CreateCheckStatusResponse binding API then returns a response that contains a Location header and additional information about the instance. Sie können die Informationen später verwenden, um den Status der gestarteten Instanz abzurufen oder sie zu beenden.You can use the information later to look up the status of the started instance or to terminate the instance.

Muster 4: ÜberwachenPattern #4: Monitor

Das Überwachen-Muster bezieht sich auf einen flexiblen, wiederkehrenden Vorgang in einem Workflow.The monitor pattern refers to a flexible, recurring process in a workflow. Ein Beispiel besteht im Abfragen, bis bestimmte Bedingungen erfüllt sind.An example is polling until specific conditions are met. Sie können einen normalen Timertrigger für ein einfaches Szenario verwenden, beispielsweise einen periodischen Bereinigungsauftrag. Sein Intervall ist jedoch statisch, und die Verwaltung der Instanzlebensdauer wird komplex.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. Mithilfe von Durable Functions können Sie flexible Wiederholungsintervalle erstellen, die Lebensdauer von Aufgaben verwalten und mehrere Überwachungsprozesse aus einer einzelnen Orchestrierung erstellen.You can use Durable Functions to create flexible recurrence intervals, manage task lifetimes, and create multiple monitor processes from a single orchestration.

Ein Beispiel für das Überwachen-Muster besteht in der Umkehrung des früheren asynchronen HTTP-API-Szenarios.An example of the monitor pattern is to reverse the earlier async HTTP API scenario. Anstatt einen Endpunkt für einen externen Client freizugeben, um einen lang laufenden Vorgang zu überwachen, belegt der lang laufende Monitor einen externen Endpunkt und wartet dann auf einen Zustandswechsel.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.

Ein Diagramm des Überwachen-Musters

Mit ein paar Codezeilen können Sie Durable Functions dazu verwenden, mehrere Monitore zu erstellen, die beliebige Endpunkte beobachten.In a few lines of code, you can use Durable Functions to create multiple monitors that observe arbitrary endpoints. Die Monitore können die Ausführung beenden, wenn eine Bedingung erfüllt ist, oder der DurableOrchestrationClient kann die Monitore beenden.The monitors can end execution when a condition is met, or the DurableOrchestrationClient can terminate the monitors. Sie können das wait-Intervall eines Monitors auf der Grundlage einer spezifischen Bedingung (z.B. exponentielles Backoff) ändern.You can change a monitor's wait interval based on a specific condition (for example, exponential backoff.)

Der folgende Code implementiert einen einfachen Monitor:The following code implements a basic monitor:

C#-SkriptC# 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 (nur 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.
});

Wenn eine Anforderung empfangen wird, wird eine neue Orchestrierungsinstanz für diese Auftrags-ID erstellt.When a request is received, a new orchestration instance is created for that job ID. Die Instanz fragt den Status ab, bis eine Bedingung erfüllt ist und die Schleife beendet wird.The instance polls a status until a condition is met and the loop is exited. Ein permanenter Timer steuert das Abrufintervall.A durable timer controls the polling interval. Anschließend können weitere Arbeitsschritte ausgeführt werden, oder die Orchestrierung wird beendet.Then, more work can be performed, or the orchestration can end. Wenn context.CurrentUtcDateTime (.NET) oder context.df.currentUtcDateTime (JavaScript) den Wert von expiryTime überschreitet, wird der Monitor beendet.When the context.CurrentUtcDateTime (.NET) or context.df.currentUtcDateTime (JavaScript) exceeds the expiryTime value, the monitor ends.

Muster 5: BenutzerinteraktionPattern #5: Human interaction

Viele automatisierte Prozesse enthalten eine Form der Benutzerinteraktion.Many automated processes involve some kind of human interaction. Das Einbeziehen von Menschen in einen automatisierten Prozess ist schwierig, da Personen nicht im gleichen hohen Maß verfügbar und reaktionsfähig sind wie Clouddienste.Involving humans in an automated process is tricky because people aren't as highly available and as responsive as cloud services. Ein automatisierter Prozess kann dem mithilfe von Timeouts und Kompensationscode Rechnung tragen.An automated process might allow for this by using timeouts and compensation logic.

Ein Genehmigungsprozess ist ein Beispiel für einen Geschäftsprozesses, der Benutzerinteraktion umfasst.An approval process is an example of a business process that involves human interaction. Beispielsweise kann für eine Spesenabrechnung, die einen bestimmten Betrag überschreitet, die Genehmigung eines Vorgesetzten erforderlich sein.Approval from a manager might be required for an expense report that exceeds a certain dollar amount. Wenn der Vorgesetzte die Spesenabrechnung nicht innerhalb von 72 Stunden genehmigt (vielleicht weil er im Urlaub ist), wird ein Eskalationsverfahren wirksam, um die Genehmigung von einer anderen Person (z.B. dem Vorgesetzten des Vorgesetzten) zu erhalten.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).

Ein Diagramm des Musters „Benutzerinteraktion“

Sie können das Muster aus diesem Beispiel mithilfe einer Orchestrierungsfunktion implementieren.You can implement the pattern in this example by using an orchestrator function. Der Orchestrator verwendet einen permanenten Timer, um die Genehmigung anzufordern.The orchestrator uses a durable timer to request approval. Der Orchestrator führt die Eskalation aus, wenn das Timeout auftritt.The orchestrator escalates if timeout occurs. Der Orchestrator wartet auf ein externes Ereignis, beispielsweise eine Benachrichtigung, die durch eine Benutzerinteraktion generiert wird.The orchestrator waits for an external event, such as a notification that's generated by a human interaction.

In diesen Beispielen wird ein Genehmigungsprozess erstellt, um das Muster der Benutzerinteraktion zu veranschaulichen:These examples create an approval process to demonstrate the human interaction pattern:

C#-SkriptC# 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 (nur 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");
    }
});

Rufen Sie zum Erstellen des permanenten Timers context.CreateTimer (.NET) oder context.df.createTimer (JavaScript) auf.To create the durable timer, call context.CreateTimer (.NET) or context.df.createTimer (JavaScript). Die Benachrichtigung wird von context.WaitForExternalEvent (.NET) oder context.df.waitForExternalEvent (JavaScript) empfangen.The notification is received by context.WaitForExternalEvent (.NET) or context.df.waitForExternalEvent (JavaScript). Anschließend wird Task.WhenAny (.NET) oder context.df.Task.any (JavaScript) aufgerufen, um zu entscheiden, ob eine Eskalation erfolgt (das Timeout tritt zuerst auf) oder die Genehmigung verarbeitet wird (die Genehmigung wird vor dem Timeout empfangen).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).

Ein externer Client kann die Ereignisbenachrichtigung entweder über die integrierten HTTP-APIs oder die DurableOrchestrationClient.RaiseEventAsync-API einer anderen Funktion an eine wartende Orchestratorfunktion senden: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);
};

Muster 6: Aggregator (Vorschauversion)Pattern #6: Aggregator (preview)

Beim sechsten Muster geht es um Aggregierung von Ereignisdaten über einen bestimmten Zeitraum in einer einzigen, adressierbaren Entität.The sixth pattern is about aggregating event data over a period of time into a single, addressable entity. In diesem Muster können die aggregierten Daten aus mehreren Quellen stammen, in Batches geliefert werden und über lange Zeiträume verteilt sein.In this pattern, the data being aggregated may come from multiple sources, may be delivered in batches, or may be scattered over long-periods of time. Der Aggregator muss möglicherweise Aktionen für Ereignisdaten durchführen, wenn er diese empfängt, und es kann sein, dass externe Daten die aggregierten Daten abfragen müssen.The aggregator might need to take action on event data as it arrives, and external clients may need to query the aggregated data.

Aggregatordiagramm

Das Schwierige an der Implementierung dieses Musters mit normalen, zustandslosen Funktionen ist die Tatsache, dass das Steuern der Parallelität zur Herausforderung wird.The tricky thing about trying to implement this pattern with normal, stateless functions is that concurrency control becomes a huge challenge. Sie müssen sich nicht nur um mehrere Threads kümmern, die gleichzeitig dieselben Daten anpassen, sondern Sie müssen auch sicherstellen, dass der Aggregator immer nur auf einer VM ausgeführt wird.Not only do you need to worry about multiple threads modifying the same data at the same time, you also need to worry about ensuring that the aggregator only runs on a single VM at a time.

Wenn Sie eine Durable Entity-Funktion verwenden, können Sie dieses Muster ohne Probleme als einzelne Funktion implementieren.Using a Durable Entity function, one can implement this pattern easily as a single function.

public static async Task Counter(
    [EntityTrigger(EntityClassName = "Counter")] IDurableEntityContext ctx)
{
    int currentValue = ctx.GetState<int>();
    int operand = ctx.GetInput<int>();

    switch (ctx.OperationName)
    {
        case "add":
            currentValue += operand;
            break;
        case "subtract":
            currentValue -= operand;
            break;
        case "reset":
            await SendResetNotificationAsync();
            currentValue = 0;
            break;
    }

    ctx.SetState(currentValue);
}

Clients können Vorgänge mit derorchestrationClient-Bindung für eine Entity-Funktion in eine Warteschlange einreihen.Clients can enqueue operations for (also known as "signaling") an entity function using the orchestrationClient binding.

[FunctionName("EventHubTriggerCSharp")]
public static async Task Run(
    [EventHubTrigger("device-sensor-events")] EventData eventData,
    [OrchestrationClient] IDurableOrchestrationClient entityClient)
{
    var metricType = (string)eventData.Properties["metric"];
    var delta = BitConverter.ToInt32(eventData.Body, eventData.Body.Offset);

    // The "Counter/{metricType}" entity is created on-demand.
    var entityId = new EntityId("Counter", metricType);
    await entityClient.SignalEntityAsync(entityId, "add", delta);
}

So ähnlich können Clients den Status einer Entity-Funktion mit Methoden in der orchestrationClient-Bindung abfragen.Similarly, clients can query for the state of an entity function using methods on the orchestrationClient binding.

Hinweis

Entity-Funktionen sind momentan nur in der Vorschauversion von Durable Functions 2.0 verfügbar.Entity functions are currently only available in the Durable Functions 2.0 preview.

Die TechnologieThe technology

Im Hintergrund baut die Erweiterung Durable Functions auf dem Durable Task Framework auf, einer Open Source-Bibliothek auf GitHub, die zum Erstellen von permanenten Aufgabenorchestrierungen verwendet wird.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. Wie Azure Functions die serverlose Weiterentwicklung von Azure WebJobs ist, so ist Durable Functions die serverlose Weiterentwicklung von 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 und andere Unternehmen verwenden das Durable Task Framework intensiv zum Automatisieren unternehmenswichtiger Prozesse.Microsoft and other organizations use the Durable Task Framework extensively to automate mission-critical processes. Es ist wie geschaffen für die serverlose Azure Functions-Umgebung.It's a natural fit for the serverless Azure Functions environment.

Ereignisherkunftsermittlung, Prüfpunkterstellung und WiedergabeEvent sourcing, checkpointing, and replay

Orchestratorfunktionen verwalten ihren Ausführungsstatus zuverlässig mithilfe eines als Ereignissourcing bezeichneten Entwurfsmusters.Orchestrator functions reliably maintain their execution state by using the event sourcing design pattern. Anstatt den aktuellen Status einer Orchestrierung direkt zu speichern, verwendet die Erweiterung Durable Functions einen ausschließlich zum Anfügen bestimmten Speicher, um die vollständige Reihe der Aktionen aufzuzeichnen, die von der Funktionsorchestrierung durchgeführt werden.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. Ein reiner Anfügespeicher bietet viele Vorteile im Vergleich mit dem „Abladen“ des gesamten Laufzeitstatus.An append-only store has many benefits compared to "dumping" the full runtime state. Zu den Vorteilen zählen die gesteigerte Leistung, Skalierbarkeit und Reaktionsfähigkeit.Benefits include increased performance, scalability, and responsiveness. Sie erhalten außerdem endgültige Konsistenz von Transaktionsdaten und vollständige Überwachungspfade sowie vollständigen Verlauf.You also get eventual consistency for transactional data and full audit trails and history. Die Überwachungspfade unterstützen zuverlässige kompensierende Aktionen.The audit trails support reliable compensating actions.

Durable Functions verwendet Ereignissourcing transparent.Durable Functions uses event sourcing transparently. Im Hintergrund gibt der await-Operator (C#) oder yield-Operator (JavaScript) in einer Orchestratorfunktion die Steuerung des Orchestratorthreads an den Durable Task Framework-Verteiler zurück.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. Der Verteiler committet dann alle neuen Aktionen, die die Orchestratorfunktion geplant hat (z.B. Aufrufen mindestens einer untergeordneten Funktion oder Planen eines permanenten Timers) in den Speicher.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. Dieser transparente Commitvorgang wird dem Ausführungsverlauf der Orchestrierungsinstanz angefügt.The transparent commit action appends to the execution history of the orchestration instance. Der Verlauf wird in einer Speichertabelle gespeichert.The history is stored in a storage table. Dann fügt die Commitaktion Nachrichten an eine Warteschlange an, um die eigentliche Arbeit zu planen.The commit action then adds messages to a queue to schedule the actual work. An diesem Punkt kann die Orchestratorfunktion aus dem Arbeitsspeicher entladen werden.At this point, the orchestrator function can be unloaded from memory.

Die Abrechnung für die Orchestratorfunktion wird beendet, wenn Sie den Verbrauchstarif für Azure Functions verwenden.Billing for the orchestrator function stops if you're using the Azure Functions consumption plan. Wenn weitere Aufgaben bewältigt werden müssen, wird die Funktion neu gestartet und ihr Status wiederhergestellt.When there's more work to do, the function restarts, and its state is reconstructed.

Wenn eine Orchestrierungsfunktion weitere Aufgaben ausführen muss (z.B. wird eine Antwortnachricht empfangen, oder ein permanenter Timer läuft ab), wird der Orchestrator reaktiviert und führt erneut die gesamte Funktion von Beginn an neu aus, um den lokalen Status wiederherzustellen.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.

Wenn der Code während dieser Wiedergabe versucht, eine Funktion aufzurufen (oder eine andere asynchrone Aktion auszuführen), zieht Durable Task Framework den Ausführungsverlauf der aktuellen Orchestrierung zu Rate.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. Wenn festgestellt wird, dass die Aktivitätsfunktion bereits ausgeführt wurde und ein Ergebnis erbracht hat, wird dieses Funktionsergebnis wiedergegeben und der Orchestratorcode weiter ausgeführt.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. Die Wiedergabe wird fortgesetzt, bis der Funktionscode abgeschlossen ist oder geplante neue asynchrone Arbeit ansteht.Replay continues until the function code is finished or until it has scheduled new async work.

Einschränkungen des OrchestratorcodesOrchestrator code constraints

Mit dem Wiedergabeverhalten von Orchestratorcode sind Einschränkungen des Codetyps verbunden, der in einer Orchestratorfunktion geschrieben werden kann.The replay behavior of orchestrator code creates constraints on the type of code that you can write in an orchestrator function. Orchestratorcode muss z.B. deterministisch sein, da er viele Male wiedergegeben wird und immer dasselbe Ergebnis produzieren muss.For example, orchestrator code must be deterministic because it will be replayed multiple times, and it must produce the same result each time. Eine vollständige Liste der Einschränkungen finden Sie unter Einschränkungen des Orchestratorcodes.For the complete list of constraints, see Orchestrator code constraints.

Überwachung und DiagnoseMonitoring and diagnostics

Die Erweiterung Durable Functions gibt automatisch strukturierte Nachverfolgungsdaten an Application Insights aus, wenn Sie Ihre Funktions-App mit einem Azure Application Insights-Instrumentierungsschlüssel eingerichtet haben.The Durable Functions extension automatically emits structured tracking data to Application Insights if you set up your function app with an Azure Application Insights instrumentation key. Sie können diese Nachverfolgungsdaten verwenden, um die Aktionen und den Status Ihrer Orchestrierungen zu überwachen.You can use the tracking data to monitor the actions and progress of your orchestrations.

Hier ist ein Beispiel für die Anzeige der Durable Functions-Nachverfolgungsereignisse im Application Insights-Portal, wenn Application Insights – Analyse verwendet wird:Here's an example of what the Durable Functions tracking events look like in the Application Insights portal when you use Application Insights Analytics:

Application Insights-Abfrageergebnisse

Sie finden nützliche strukturierte Daten im Feld customDimensions in jedem Protokolleintrag.You can find useful structured data in the customDimensions field in each log entry. Hier ist ein Beispiel für einen vollständig aufgeklappten Eintrag:Here's an example of an entry that is fully expanded:

Das customDimensions-Feld in einer Application Insights-Abfrage

Aufgrund des Wiedergabeverhaltens des Durable Task Framework-Verteilers können Sie davon ausgehen, redundante Protokolleinträge für wiedergegebene Aktionen zu sehen.Because of the replay behavior of the Durable Task Framework dispatcher, you can expect to see redundant log entries for replayed actions. Redundante Protokolleinträge können Ihnen helfen, das Wiedergabeverhalten der Kern-Engine zu verstehen.Redundant log entries can help you understand the replay behavior of the core engine. Der Artikel Diagnostics in Durable Functions (Azure Functions) (Diagnose in Durable Functions [Azure Functions]) zeigt Beispiele für Abfragen, die Wiedergabeprotokolle ausfiltern, sodass Sie nur die „Echtzeitprotokolle“ sehen.The Diagnostics article shows sample queries that filter out replay logs, so you can see just the "real-time" logs.

Speicherung und SkalierbarkeitStorage and scalability

Die Erweiterung Durable Functions verwendet Warteschlangen, Tabellen und Blobs in Azure Storage, um Ausführungsverlaufsstatus und Triggerfunktionsausführung persistent zu speichern.The Durable Functions extension uses queues, tables, and blobs in Azure Storage to persist execution history state and trigger function execution. Sie können das Standardspeicherkonto für die Funktions-App nutzen oder ein separates Speicherkonto konfigurieren.You can use the default storage account for the function app, or you can configure a separate storage account. Abhängig von den Grenzwerten für den Speicherdurchsatz sollten Sie ggf. ein separates Konto verwenden.You might want a separate account based on storage throughput limits. Der Orchestratorcode, den Sie schreiben, interagiert nicht mit den Entitäten in diesen Speicherkonten.The orchestrator code you write doesn't interact with the entities in these storage accounts. Das Durable Task Framework verwaltet die Entitäten direkt als Implementierungsdetail.The Durable Task Framework manages the entities directly as an implementation detail.

Orchestratorfunktionen planen Aktivitätsfunktionen und empfangen ihre Antworten über interne Warteschlangennachrichten.Orchestrator functions schedule activity functions and receive their responses via internal queue messages. Wenn eine Funktions-App im Azure Functions-Verbrauchstarif ausgeführt wird, werden diese Warteschlangen vom Azure Functions-Skalierungscontroller überwacht.When a function app runs in the Azure Functions consumption plan, the Azure Functions scale controller monitors these queues. Neue Compute-Instanzen werden nach Bedarf hinzugefügt.New compute instances are added as needed. Beim horizontalen Hochskalieren über mehrere virtuelle Computer kann eine Orchestratorfunktion auf einem virtuellen Computer ausgeführt werden, während Aktivitätsfunktionen, die von der Orchestratorfunktion aufgerufen werden, auf mehreren anderen virtuellen Computern ausgeführt werden.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. Weitere Informationen über das Skalierungsverhalten von Durable Functions finden Sie unter Performance and scale in Durable Functions (Azure Functions) (Leistung und Skalierung in Durable Functions [Azure Functions]).For more information about the scale behavior of Durable Functions, see Performance and scale.

Der Ausführungsverlauf für Orchestratorkonten wird in Tabellenspeicher gespeichert.The execution history for orchestrator accounts is stored in table storage. Immer dann, wenn eine Instanz auf einem bestimmten virtuellen Computer aktiviert wird, ruft der Orchestrator ihren Ausführungsverlauf vom Tabellenspeicher ab, damit er ihren lokalen Zustand wiederherstellen kann.Whenever an instance rehydrates on a particular VM, the orchestrator fetches its execution history from table storage so it can rebuild its local state. Ein komfortabler Aspekt des Vorliegens des Verlaufs in Tabellenspeicher liegt darin, dass Sie Tools wie Azure Storage Explorer verwenden können, um den Verlauf Ihrer Orchestrierungen anzuzeigen.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.

Speicher-Blobs werden hauptsächlich als Leasingmechanismus zum Koordinieren der übergreifenden horizontalen Skalierung von Orchestrierungsinstanzen über mehrere VMs hinweg verwendet.Storage blobs are primarily used as a leasing mechanism to coordinate the scale-out of orchestration instances across multiple VMs. Speicher-Blobs speichern Daten für umfangreiche Nachrichten, die nicht direkt in Tabellen oder Warteschlangen gespeichert werden können.Storage blobs hold data for large messages that can't be stored directly in tables or queues.

Screenshot von Azure Storage-Explorer

Hinweis

Es ist zwar einfach und bequem, den Ausführungsverlauf im Tabellenspeicher angezeigt zu bekommen, Sie sollten aber keine Abhängigkeiten von dieser Tabelle einrichten.Although it's easy and convenient to see execution history in table storage, don't make any dependencies on this table. Die Tabelle kann sich im Rahmen der Weiterentwicklung der Erweiterung Durable Functions ändern.The table might change as the Durable Functions extension evolves.

Bekannte ProblemeKnown issues

Alle bekannten Probleme sollten in der Liste der GitHub-Probleme nachverfolgt werden.All known issues should be tracked in the GitHub issues list. Wenn Sie auf ein Problem stoßen, das in GitHub nicht zu finden ist, öffnen Sie ein neues Problem.If you run into a problem and can't find the issue in GitHub, open a new issue. Schließen Sie eine ausführliche Beschreibung des Problems ein.Include a detailed description of the problem.

Nächste SchritteNext steps

Weitere Informationen zu Durable Functions finden Sie im Artikel zu Funktionstypen und Features von Durable Functions.To learn more about Durable Functions, see Durable Functions function types and features.

Erste Schritte:To get started: