Freigeben über


Behandeln externer Ereignisse

Orchestratorfunktionen können warten und auf externe Ereignisse lauschen. Dieses Feature von Durable Functions ist häufig hilfreich für die Behandlung menschlicher Interaktionen oder anderer externer Trigger.

Hinweis

Externe Ereignisse sind unidirektionale asynchrone Vorgänge. Sie eignen sich nicht für Situationen, in denen der Client, der das Ereignis sendet, eine synchrone Antwort von der Orchestratorfunktion benötigt.

Orchestrierungen können warten und auf externe Ereignisse lauschen. Dieses Feature ist häufig nützlich für die Behandlung von Interaktionen zwischen Menschen oder anderen externen Triggern.

Hinweis

Externe Ereignisse sind unidirektionale asynchrone Vorgänge. Sie sind nicht geeignet für Situationen, in denen der Client, der das Ereignis sendet, eine synchrone Antwort von der Orchestration benötigt.

Von Bedeutung

Derzeit ist das PowerShell Durable Task SDK nicht verfügbar.

Auf Ereignisse warten

Die API "wait-for-external-event" der Orchestrierungstriggerbindung ermöglicht es einer Orchestratorfunktion, asynchron zu warten und auf ein Ereignis zu lauschen, das von einem externen Client bereitgestellt wird. Die Listening Orchestrator-Funktion deklariert den Namen des Ereignisses und die Form der Daten, die sie empfangen soll.

Die API "wait-for-external-event" ermöglicht es einer Orchestrierung, asynchron zu warten und auf ein Ereignis zu lauschen, das von einem externen Client bereitgestellt wird. Die Hörorchestrierung deklariert den Namen des Ereignisses und die Form der Daten, die sie zu empfangen erwartet.

Isoliertes Arbeitermodell

using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;

public class BudgetApproval
{
    private readonly ILogger _logger;

    public BudgetApproval(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<BudgetApproval>();
    }

    [Function("BudgetApproval")]
    public async Task Run(
        [OrchestrationTrigger] TaskOrchestrationContext context)
    {
        bool approved = await context.WaitForExternalEventAsync<bool>("Approval");
        if (approved)
        {
            // approval granted - do the approved action
        }
        else
        {
            // approval denied - send a notification
        }
    }
}

In-Process-Modell

[FunctionName("BudgetApproval")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool approved = await context.WaitForExternalEvent<bool>("Approval");
    if (approved)
    {
        // approval granted - do the approved action
    }
    else
    {
        // approval denied - send a notification
    }
}

Hinweis

Wenn Sie Durable Functions 1.x verwenden, verwenden Sie DurableOrchestrationContext anstelle von IDurableOrchestrationContext. Schauen Sie sich den Artikel über Durable Functions-Versionen an, um weitere versionsspezifische Details zu erhalten.

public class BudgetApproval : TaskOrchestrator<object?, bool>
{
    public override async Task<bool> RunAsync(TaskOrchestrationContext context, object? input)
    {
        bool approved = await context.WaitForExternalEvent<bool>("Approval");
        if (approved)
        {
            // approval granted - do the approved action
        }
        else
        {
            // approval denied - send a notification
        }
        return approved;
    }
}

Im vorherigen Beispiel wird auf ein bestimmtes einzelnes Ereignis gelauscht und eine Aktion ausgeführt, wenn das Ereignis empfangen wird.

Sie können mehrere Ereignisse gleichzeitig überwachen, z. B. im folgenden Beispiel, das auf eine von drei möglichen Ereignisbenachrichtigungen wartet.

Isoliertes Arbeitermodell

[Function("Select")]
public async Task Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    Task<float> event1 = context.WaitForExternalEventAsync<float>("Event1");
    Task<bool> event2 = context.WaitForExternalEventAsync<bool>("Event2");
    Task<int> event3 = context.WaitForExternalEventAsync<int>("Event3");

    Task winner = await Task.WhenAny(event1, event2, event3);
    if (winner == event1)
    {
        // ...
    }
    else if (winner == event2)
    {
        // ...
    }
    else if (winner == event3)
    {
        // ...
    }
}

In-Process-Modell

[FunctionName("Select")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var event1 = context.WaitForExternalEvent<float>("Event1");
    var event2 = context.WaitForExternalEvent<bool>("Event2");
    var event3 = context.WaitForExternalEvent<int>("Event3");

    var winner = await Task.WhenAny(event1, event2, event3);
    if (winner == event1)
    {
        // ...
    }
    else if (winner == event2)
    {
        // ...
    }
    else if (winner == event3)
    {
        // ...
    }
}

Hinweis

Verwenden Sie Durable Functions 1.x? Verwenden Sie DurableOrchestrationContext anstelle von IDurableOrchestrationContext. Weitere Informationen zu anderen Versionsunterschieden finden Sie im Artikel Durable Functions.

public class SelectOrchestrator : TaskOrchestrator<object?, object?>
{
    public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
    {
        Task<float> event1 = context.WaitForExternalEvent<float>("Event1");
        Task<bool> event2 = context.WaitForExternalEvent<bool>("Event2");
        Task<int> event3 = context.WaitForExternalEvent<int>("Event3");

        Task winner = await Task.WhenAny(event1, event2, event3);
        if (winner == event1)
        {
            // ...
        }
        else if (winner == event2)
        {
            // ...
        }
        else if (winner == event3)
        {
            // ...
        }
        return null;
    }
}

Im vorherigen Beispiel wird auf alle von mehreren Ereignissen gelauscht. Sie können auch auf alle Ereignisse warten.

Isoliertes Arbeitermodell

[Function("NewBuildingPermit")]
public async Task Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    string applicationId = context.GetInput<string>();

    Task gate1 = context.WaitForExternalEventAsync<object>("CityPlanningApproval");
    Task gate2 = context.WaitForExternalEventAsync<object>("FireDeptApproval");
    Task gate3 = context.WaitForExternalEventAsync<object>("BuildingDeptApproval");

    // all three departments must grant approval before a permit can be issued
    await Task.WhenAll(gate1, gate2, gate3);

    await context.CallActivityAsync("IssueBuildingPermit", applicationId);
}

In-Process-Modell

[FunctionName("NewBuildingPermit")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string applicationId = context.GetInput<string>();

    var gate1 = context.WaitForExternalEvent("CityPlanningApproval");
    var gate2 = context.WaitForExternalEvent("FireDeptApproval");
    var gate3 = context.WaitForExternalEvent("BuildingDeptApproval");

    // all three departments must grant approval before a permit can be issued
    await Task.WhenAll(gate1, gate2, gate3);

    await context.CallActivityAsync("IssueBuildingPermit", applicationId);
}

Hinweis

Wenn Sie Durable Functions 1.x ausführen, verwenden Sie DurableOrchestrationContext anstelle von IDurableOrchestrationContext. Wechseln Sie zu Durable Functions-Versionen für eine vollständige Aufschlüsselung der Versionsunterschiede.

Wenn die Ereignisnutzlast in .NET nicht in den erwarteten Typ T konvertiert werden kann, wird eine Ausnahme ausgelöst.

public class NewBuildingPermit : TaskOrchestrator<string, object?>
{
    public override async Task<object?> RunAsync(TaskOrchestrationContext context, string applicationId)
    {
        Task<object?> gate1 = context.WaitForExternalEvent<object?>("CityPlanningApproval");
        Task<object?> gate2 = context.WaitForExternalEvent<object?>("FireDeptApproval");
        Task<object?> gate3 = context.WaitForExternalEvent<object?>("BuildingDeptApproval");

        // all three departments must grant approval before a permit can be issued
        await Task.WhenAll(gate1, gate2, gate3);

        await context.CallActivityAsync("IssueBuildingPermit", applicationId);
        return null;
    }
}

Wenn die Ereignisnutzlast in .NET nicht in den erwarteten Typ T konvertiert werden kann, wird eine Ausnahme ausgelöst.

Die API "wait-for-external-event" wartet auf unbestimmte Zeit auf einige Eingaben. Sie können die Funktions-App beim Warten sicher entladen. Wenn und wenn ein Ereignis für diese Orchestrierungsinstanz eintrifft, wird die Instanz automatisch aktiviert und verarbeitet das Ereignis sofort.

Hinweis

Wenn Ihre Funktions-App den Verbrauchsplan verwendet, entstehen keine Abrechnungsgebühren, während eine Orchestratorfunktion auf eine externe Ereignisaufgabe wartet, unabhängig davon, wie lange sie wartet.

Wie bei Aktivitätsfunktionen weisen externe Ereignisse eine Garantie von mindestens einer Zustellung auf. Dies bedeutet, dass Ihre Anwendung unter bestimmten Bedingungen (z. B. Neustarts, Skalierung, Abstürze usw.) Duplikate desselben externen Ereignisses empfängt. Daher wird empfohlen, dass externe Ereignisse eine Art von ID enthalten, mit der sie in Orchestratoren manuell dedupliziert werden können.

Die API "wait-for-external-event" wartet auf unbestimmte Zeit auf einige Eingaben. Sie können den Mitarbeiter während des Wartens sicher beenden. Wenn und wenn ein Ereignis für diese Orchestrierungsinstanz eintrifft, wird es automatisch aktiviert und verarbeitet das Ereignis sofort.

Externe Ereignisse haben mindestens einmal eine Liefergarantie. Dies bedeutet, dass Ihre Anwendung unter bestimmten Bedingungen (z. B. Neustarts, Skalierung, Abstürze usw.) Duplikate desselben externen Ereignisses empfängt. Daher empfehlen wir, dass externe Ereignisse eine Art ID enthalten, die es ermöglicht, sie in Orchestrierungen manuell zu deduplizieren.

Senden von Ereignissen

Sie können die durch die Orchestrierungsclientbindung definierte "raise-event"-API verwenden, um ein externes Ereignis an eine Orchestrierung zu senden. Sie können auch die integrierte HTTP-API zum Erzeugen eines Ereignisses verwenden, um ein externes Ereignis an eine Orchestrierung zu senden.

Ein ausgelöstes Ereignis enthält ein instanceID, ein eventName und eventData als Parameter. Orchestrator-Funktionen behandeln diese Ereignisse mithilfe der wait-for-external-event APIs. Die eventName Übereinstimmung muss sowohl auf der Sendeseite als auch auf der Empfangsseite gegeben sein, damit das Ereignis verarbeitet werden kann. Die Ereignisdaten müssen auch JSON-serialisierbar sein.

Intern reihen die raise event-Mechanismen eine Nachricht in die Warteschlange ein, die von der wartenden Orchestratorfunktion aufgegriffen wird. Wenn die Instanz nicht auf den angegebenen Ereignisnamen wartet, wird die Ereignismeldung einer Speicherwarteschlange hinzugefügt. Wenn die Orchestrierungsinstanz später beginnt, auf diesen Ereignisnamen zu lauschen, überprüft sie die Warteschlange auf Ereignismeldungen.

Hinweis

Wenn keine Orchestrierungsinstanz mit der angegebenen Instanz-ID vorhanden ist, wird die Ereignismeldung verworfen.

Nachfolgend finden Sie ein Beispiel für eine in der Warteschlange ausgelöste Funktion, die ein "Approval"-Ereignis an eine Orchestrator-Funktionsinstanz sendet. Die Orchestrierungsinstanz-ID stammt aus dem Text der Warteschlangennachricht.

Sie können die "raise-event"- API auf dem Durable Task-Client verwenden, um ein externes Ereignis an eine Orchestrierung zu senden.

Ein ausgelöstes Ereignis enthält eine Instanz-ID, einen eventName und eventData als Parameter. Orchestrierungen verarbeiten diese Ereignisse mithilfe der „wait-for-external-event”-APIs. Der EventName muss sowohl für das Senden als auch für den Empfang übereinstimmen, damit das Ereignis verarbeitet werden kann. Die Ereignisdaten müssen auch JSON-serialisierbar sein.

Intern reihen die „raise event”-Mechanismen eine Nachricht in die Warteschlange ein, die von der wartenden Orchestrierung aufgegriffen wird. Wenn die Instanz nicht auf den angegebenen Ereignisnamen wartet, wird die Ereignismeldung einer Speicherwarteschlange hinzugefügt. Wenn die Orchestrierungsinstanz später mit der Überwachung dieses Ereignisnamens beginnt, wird sie die Warteschlange auf Ereignismeldungen überprüfen.

Hinweis

Wenn mit der angegebenen instanceID keine Orchestrierungsinstanz vorhanden ist, wird die Ereignismeldung verworfen.

Nachfolgend sehen Sie ein Beispiel, das ein "Approval"-Ereignis an eine Orchestrierungsinstanz sendet.

Isoliertes Arbeitermodell

using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask.Client;

public class ApprovalQueueProcessor
{
    [Function("ApprovalQueueProcessor")]
    public async Task Run(
        [QueueTrigger("approval-queue")] string instanceId,
        [DurableClient] DurableTaskClient client)
    {
        await client.RaiseEventAsync(instanceId, "Approval", true);
    }
}

In-Process-Modell

[FunctionName("ApprovalQueueProcessor")]
public static async Task Run(
    [QueueTrigger("approval-queue")] string instanceId,
    [DurableClient] IDurableOrchestrationClient client)
{
    await client.RaiseEventAsync(instanceId, "Approval", true);
}

Hinweis

Verwenden Sie für Durable Functions 1.x stattdessen das Attribut OrchestrationClient und DurableOrchestrationClient Parametertyp. Überprüfen Sie den Artikel Durable Functions versions auf alle versionsspezifischen Änderungen.

Intern reiht die raise event-API eine Nachricht in die Warteschlange ein, die von der wartenden Orchestratorfunktion aufgegriffen wird. Wenn die Instanz nicht auf den angegebenen Ereignisnamen wartet, wird die Ereignismeldung einem Speicherpuffer hinzugefügt. Wenn die Orchestrierungsinstanz später mit der Überwachung dieses Ereignisnamens beginnt, überprüft sie den Puffer auf Ereignismeldungen und löst die Aufgabe aus, die darauf wartet.

Hinweis

Wenn keine Orchestrierungsinstanz mit der angegebenen Instanz-ID vorhanden ist, wird die Ereignismeldung verworfen.

await client.RaiseEventAsync(instanceId, "Approval", true);

Intern reiht die „raise event”-API eine Nachricht in die Warteschlange ein, die von der wartenden Orchestrierung aufgegriffen wird. Wenn die Instanz nicht auf den angegebenen Ereignisnamen wartet, wird die Ereignismeldung einem Speicherpuffer hinzugefügt. Wenn die Orchestrierungsinstanz später beginnt, auf diesen Ereignisnamen zu lauschen, wird der Puffer auf Ereignismeldungen überprüft und die Aufgabe ausgelöst, die darauf wartete.

Hinweis

Wenn keine Orchestrierungsinstanz mit der angegebenen Instanz-ID vorhanden ist, wird die Ereignismeldung verworfen.

HTTP

Nachfolgend sehen Sie ein Beispiel für eine HTTP-Anforderung, die ein Approval Ereignis an eine Orchestrierungsinstanz auslöst.

POST /runtime/webhooks/durabletask/instances/MyInstanceId/raiseEvent/Approval&code=XXX
Content-Type: application/json

"true"

In diesem Fall wird die Instanz-ID als MyInstanceId hartcodiert.

Bewährte Methoden für externe Ereignisse

Beachten Sie beim Arbeiten mit externen Ereignissen die folgenden bewährten Methoden:

Verwenden eindeutiger Ereignisnamen für die Deduplizierung

Externe Ereignisse haben mindestens einmal eine Liefergarantie. Unter bestimmten seltenen Bedingungen (die während Neustarts, Skalierung oder Abstürze auftreten können), empfängt Ihre Anwendung möglicherweise Duplikate desselben externen Ereignisses. Es wird empfohlen, dass externe Ereignisse eine eindeutige ID enthalten, mit der sie manuell in Orchestratoren dedupliziert werden können.

Hinweis

Der MSSQL-Speicheranbieter konsumiert externe Ereignisse und aktualisiert den Orchestrator-Zustand transaktional, sodass im Gegensatz zum Azure Storage-Backend kein Risiko von doppelten Ereignissen besteht. Es wird jedoch weiterhin empfohlen, dass externe Ereignisse eindeutige Namen haben, sodass Code über Back-End-Geräte hinweg portierbar ist.

Nächste Schritte