Compartir a través de


Control de eventos externos

Las funciones de orquestador pueden esperar y escuchar eventos externos. Esta característica de Durable Functions suele ser útil para controlar la interacción humana u otros desencadenadores externos.

Nota:

Los eventos externos son operaciones asincrónicas unidireccionales. No son adecuados para situaciones en las que el cliente que envía el evento necesita una respuesta sincrónica de la función de orquestador.

Las orquestaciones pueden esperar y escuchar eventos externos. Esta característica suele ser útil para controlar la interacción humana u otros desencadenadores externos.

Nota:

Los eventos externos son operaciones asincrónicas unidireccionales. No son adecuados para situaciones en las que el cliente que envía el evento necesita una respuesta sincrónica de la orquestación.

Importante

Actualmente, el SDK de Durable Task de PowerShell no está disponible.

Esperar los eventos

La API "wait-for-external-event" del enlace del desencadenador de orquestación permite que una función del orquestador espere y escuche de forma asíncrona un evento entregado por un cliente externo. La función de orquestador de escucha declara el nombre del evento y la forma de los datos que espera recibir.

La API "wait-for-external-event" permite que una orquestación espere y escuche de forma asincrónica un evento entregado por un cliente externo. La orquestación de escucha declara el nombre del evento y la forma de los datos que espera recibir.

Modelo de trabajo aislado

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
        }
    }
}

Modelo de proceso

[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
    }
}

Nota:

Si usa Durable Functions 1.x, use DurableOrchestrationContext en lugar de IDurableOrchestrationContext. Consulte el artículo versiones de Durable Functions para obtener más detalles específicos sobre las versiones.

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;
    }
}

En el ejemplo anterior se escucha un evento único específico y se realiza una acción cuando se recibe el evento.

Puede escuchar varios eventos simultáneamente, como en el ejemplo siguiente, que espera una de las tres notificaciones de eventos posibles.

Modelo de trabajo aislado

[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)
    {
        // ...
    }
}

Modelo de proceso

[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)
    {
        // ...
    }
}

Nota:

¿Está utilizando Durable Functions 1.x? Intercambiar en en lugar de . Consulte el artículo Durable Functions versiones para obtener información sobre otras diferencias de versiones.

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;
    }
}

El ejemplo anterior escucha cualquiera de varios eventos posibles. También puede esperar a todos los eventos.

Modelo de trabajo aislado

[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);
}

Modelo de proceso

[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);
}

Nota:

Si ejecuta Durable Functions 1.x, use DurableOrchestrationContext en lugar de IDurableOrchestrationContext. Vaya a Durable Functions versiones para obtener un desglose completo de las diferencias de versión.

En .NET, si no se puede convertir la carga del evento en el tipo esperado T, se produce una excepción.

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;
    }
}

En .NET, si no se puede convertir la carga del evento en el tipo esperado T, se produce una excepción.

La API "wait-for-external-event" espera indefinidamente para alguna entrada. Puede descargar de forma segura la aplicación de funciones mientras espera. Si y cuando llega un evento para esta instancia de orquestación, la instancia se despierta automáticamente e inmediatamente procesa el evento.

Nota:

Si la aplicación de funciones utiliza el Plan de Consumo, no se incurren en cargos de facturación mientras una función de orquestación espera un evento externo, sin importar cuánto tiempo tenga que esperar.

Al igual que con las Funciones de actividad, los eventos externos tienen una garantía de entrega de al menos una vez. Esto significa que, en determinadas condiciones (como reinicios, escalado, bloqueos, etc.), la aplicación puede recibir duplicados del mismo evento externo. Por lo tanto, recomendamos que los eventos externos contengan algún tipo de identificador que permita su desduplicación manual en los orquestadores.

La API "wait-for-external-event" espera indefinidamente para alguna entrada. Puede detener de forma segura al trabajador mientras espera. Si y cuando llega un evento para esta instancia de orquestación, se despierta automáticamente e inmediatamente procesa el evento.

Los eventos externos tienen una garantía de entrega al menos una vez . Esto significa que, en determinadas condiciones (como reinicios, escalado, bloqueos, etc.), la aplicación puede recibir duplicados del mismo evento externo. Por lo tanto, se recomienda que los eventos externos contengan algún tipo de identificador que les permita desduplicarse manualmente en orquestaciones.

Envío de eventos

Puede usar la API "raise-event" definida por el cliente de orquestación para enviar un evento externo a una orquestación. También puede usar la API de HTTP de generación de evento integrada para enviar un evento externo a una orquestación.

Un evento generado incluye un , un y como parámetros. Las funciones de Orquestador controlan estos eventos mediante las API. debe coincidir en los extremos de envío y recepción para que el evento se procese. Los datos del evento también deben ser serializables por JSON.

De manera interna, los mecanismos de "raise-event" ponen en cola un mensaje que recoge la función de orquestador en espera. Si la instancia no está esperando el nombre de evento especificado, el mensaje de evento se agrega a una cola en memoria. Si la instancia de orquestación comienza a escuchar más adelante ese nombre de evento, comprueba la cola de mensajes de evento.

Nota:

Si no hay ninguna instancia de orquestación con el identificador de instancia especificado, se descarta el mensaje de evento.

A continuación se muestra una función desencadenada por la cola de ejemplo que envía un evento de aprobación a una instancia de la función de orquestador. El identificador de instancia de orquestación procede del cuerpo del mensaje de cola.

Puede usar la API "raise-event" en el cliente de Durable Task para enviar un evento externo a una orquestación.

Un evento generado incluye un identificador de instancia, un eventName y eventData como parámetros. Las orquestaciones gestionan estos eventos mediante la API "wait-for-external-event". EventName debe coincidir en los extremos de envío y recepción para que el evento se procese. Los datos del evento también deben ser serializables por JSON.

Internamente, los mecanismos "raise-event" ponen en cola un mensaje que la orquestación en espera recoge. Si la instancia no está esperando el nombre de evento especificado, el mensaje del evento se agrega a una cola en memoria. Si la instancia de orquestación comienza más adelante a escuchar ese nombre de evento, comprobará la cola de mensajes de evento.

Nota:

Si no hay ninguna instancia de orquestación con el especificado , se descarta el mensaje de evento.

A continuación se muestra un ejemplo que envía un evento "Approval" a una instancia de orquestación.

Modelo de trabajo aislado

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);
    }
}

Modelo de proceso

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

Nota:

Para Durable Functions 1.x, utilice el atributo OrchestrationClient y el tipo de parámetro DurableOrchestrationClient en su caso. Consulte el artículo versiones de Durable Functions para ver todos los cambios específicos de la versión.

Internamente, la API "raise-event" pone en cola un mensaje que es recogido por la función del orquestador en espera. Si la instancia no está esperando el nombre de evento especificado, el mensaje de evento se agrega a un búfer en memoria. Si la instancia de orquestación comienza a escuchar más adelante ese nombre de evento, comprueba el búfer de los mensajes de evento y desencadena la tarea que estaba esperando.

Nota:

Si no hay ninguna instancia de orquestación con el identificador de instancia especificado, se descarta el mensaje de evento.

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

Internamente, la API llamada "raise-event" encola un mensaje que es recogido por la orquestación que está en espera. Si la instancia no está esperando el nombre de evento especificado, el mensaje de evento se agrega a un búfer en memoria. Si la instancia de orquestación comienza a escuchar ese nombre de evento más tarde, comprobará el búfer en busca de mensajes de evento y activará la tarea que estaba esperando.

Nota:

Si no hay ninguna instancia de orquestación con el identificador de instancia especificado, se descarta el mensaje de evento.

HTTP

A continuación se muestra un ejemplo de una solicitud HTTP que genera un evento en una instancia de orquestación.

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

"true"

En este caso, el identificador de instancia se codifica de forma dura como MyInstanceId.

Procedimientos recomendados para eventos externos

Tenga en cuenta los siguientes procedimientos recomendados al trabajar con eventos externos:

Uso de nombres de eventos únicos para la desduplicación

Los eventos externos tienen una garantía de entrega al menos una vez . En determinadas condiciones poco frecuentes (que pueden producirse durante los reinicios, el escalado o los bloqueos), la aplicación podría recibir duplicados del mismo evento externo. Se recomienda que los eventos externos contengan un identificador único que les permita desduplicarse manualmente en orquestadores.

Nota:

El proveedor de almacenamiento MSSQL consume eventos externos y actualiza el estado del orquestador de forma transaccional, por lo que no hay ningún riesgo de eventos duplicados con ese back-end, a diferencia del proveedor Azure Storage. Sin embargo, todavía se recomienda que los eventos externos tengan nombres únicos para que el código sea portátil en los back-end.

Pasos siguientes

Aprenda a implementar el control de errores

Ejecutar una muestra que espera la interacción humana

Introducción a los SDK de Durable Task