O que é a Durable Functions?

Durable Functions é uma extensão de Funções do Azure que lhe permite escrever funções com estado num ambiente de computação sem servidor. A extensão permite-lhe definir fluxos de trabalho com monitorização de estado ao escrever funções de orquestrador e entidades com monitorização de estado ao escrever funções de entidade com o modelo de programação Funções do Azure. Nos bastidores, a extensão gere o estado, os pontos de verificação e os reinícios automaticamente, permitindo-lhe concentrar-se na lógica de negócio.

Linguagens suportadas

Durable Functions foi concebido para trabalhar com todas as linguagens de programação Funções do Azure, mas pode ter requisitos mínimos diferentes para cada linguagem. A tabela seguinte mostra as configurações mínimas suportadas da aplicação:

Pilha de idiomas Funções do Azure versões do Runtime Versão da função de trabalho de idioma Versão mínima dos pacotes
.NET/C# / F # Funções 1.0+ Em processo (GA)
Fora do processo (pré-visualização)
n/a
JavaScript/TypeScript Funções 2.0+ Nó 8+ 2.x pacotes
Python Funções 2.0+ Python 3.7 e superior 2.x pacotes
PowerShell Funções 3.0+ PowerShell 7+ 2.x pacotes
Java Funções 4.0+ Java 8+ 4.x pacotes

Tal como Funções do Azure, existem modelos que o ajudam a desenvolver Durable Functions com o Visual Studio 2019, o Visual Studio Code e o portal do Azure.

Padrões de aplicação

O principal caso de utilização para Durable Functions é simplificar requisitos de coordenação complexos e com monitorização de estado em aplicações sem servidor. As secções seguintes descrevem padrões de aplicação típicos que podem beneficiar de Durable Functions:

Padrão n.º 1: Encadeamento de funções

No padrão de encadeamento de funções, uma sequência de funções é executada por uma ordem específica. Neste padrão, a saída de uma função é aplicada à entrada de outra função. A utilização de filas entre cada função garante que o sistema permanece durável e dimensionável, apesar de existir um fluxo de controlo de uma função para a outra.

Um diagrama do padrão de encadeamento de funções

Pode utilizar Durable Functions para implementar o padrão de encadeamento de funções de forma concisa, conforme mostrado no exemplo seguinte.

Neste exemplo, os valores F1, F2, F3e F4 são os nomes de outras funções na mesma aplicação de funções. Pode implementar o fluxo de controlo com construções normais de codificação imperativa. O código é executado de cima para baixo. O código pode envolver semântica de fluxo de controlo de linguagem existente, como condicionais e ciclos. Pode incluir lógica de processamento de erros em try//catchfinally blocos.

[FunctionName("Chaining")]
public static async Task<object> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    try
    {
        var x = await context.CallActivityAsync<object>("F1", null);
        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.
    }
}

Pode utilizar o context parâmetro para invocar outras funções por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama await, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior await . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.

Padrão n.º 2: Fan out/fan in

No padrão fan out/fan in, executa várias funções em paralelo e, em seguida, aguarda que todas as funções terminem. Muitas vezes, alguns trabalhos de agregação são realizados nos resultados que são devolvidos das funções.

Um diagrama do padrão fan out/fan

Com as funções normais, pode destacar-se ao fazer com que a função envie várias mensagens para uma fila. Voltar a entrar é muito mais desafiante. Para ativar a ventoinha, numa função normal, escreve código para controlar quando as funções acionadas pela fila terminam e, em seguida, armazena as saídas da função.

A extensão Durable Functions processa este padrão com código relativamente simples:

[FunctionName("FanOutFanIn")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext 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", null);
    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);
}

O trabalho de fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. Task.WhenAll é chamado para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.

O ponto de verificação automático que ocorre na await chamada em Task.WhenAll garante que uma possível falha a meio do caminho ou reinício não requer o reinício de uma tarefa já concluída.

Nota

Em circunstâncias raras, é possível que uma falha possa ocorrer na janela após a conclusão de uma função de atividade, mas antes da conclusão ser guardada no histórico de orquestração. Se isto acontecer, a função de atividade será novamente executada desde o início após a recuperação do processo.

Padrão n.º 3: APIs HTTP assíncronas

O padrão da API HTTP assíncrona resolve o problema de coordenar o estado das operações de execução prolongada com clientes externos. Uma forma comum de implementar este padrão é ter um ponto final HTTP acionar a ação de execução prolongada. Em seguida, redirecione o cliente para um ponto final de estado que o cliente consulta para saber quando a operação está concluída.

Um diagrama do padrão da API HTTP

Durable Functions fornece suporte incorporado para este padrão, simplificando ou mesmo removendo o código que precisa de escrever para interagir com execuções de funções de execução prolongada. Por exemplo, os Durable Functions exemplos de início rápido (C#, JavaScript, Python, PowerShell e Java) mostram um comando REST simples que pode utilizar para iniciar novas instâncias de função do orquestrador. Após o início de uma instância, a extensão expõe as APIs HTTP do webhook que consultam o estado da função do orquestrador.

O exemplo seguinte mostra comandos REST que iniciam um orquestrador e consultam o respetivo estado. Para maior clareza, alguns detalhes do protocolo são omitidos do exemplo.

> curl -X POST https://myfunc.azurewebsites.net/api/orchestrators/DoWork -H "Content-Length: 0" -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec

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

> curl https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec

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

> curl https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 200 OK
Content-Length: 175
Content-Type: application/json

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

Uma vez que o Durable Functions runtime gere o seu estado, não precisa de implementar o seu próprio mecanismo de controlo de estado.

A extensão Durable Functions expõe AS APIs HTTP incorporadas que gerem orquestrações de longa duração. Em alternativa, pode implementar este padrão utilizando os seus próprios acionadores de função (como HTTP, uma fila ou Hubs de Eventos do Azure) e o enlace do cliente de orquestração. Por exemplo, pode utilizar uma mensagem de fila para acionar a terminação. Em alternativa, pode utilizar um acionador HTTP protegido por uma política de autenticação do Azure Active Directory em vez das APIs HTTP incorporadas que utilizam uma chave gerada para autenticação.

Para obter mais informações, consulte o artigo funcionalidades HTTP, que explica como pode expor processos assíncronos e de execução prolongada através de HTTP com a extensão Durable Functions.

Padrão n.º 4: Monitorizar

O padrão de monitorização refere-se a um processo flexível e periódico num fluxo de trabalho. Um exemplo é consultar até que sejam cumpridas condições específicas. Pode utilizar um acionador de temporizador regular para abordar um cenário básico, como uma tarefa de limpeza periódica, mas o intervalo é estático e a gestão de durações de instâncias torna-se complexa. Pode utilizar Durable Functions para criar intervalos de periodicidade flexíveis, gerir durações de tarefas e criar vários processos de monitorização a partir de uma única orquestração.

Um exemplo do padrão de monitorização é inverter o cenário da API HTTP assíncrona anterior. Em vez de expor um ponto final para um cliente externo monitorizar uma operação de execução prolongada, o monitor de execução prolongada consome um ponto final externo e, em seguida, aguarda uma alteração de estado.

Um diagrama do padrão de monitorização

Em algumas linhas de código, pode utilizar Durable Functions para criar vários monitores que observam pontos finais arbitrários. Os monitores podem terminar a execução quando uma condição é cumprida ou outra função pode utilizar o cliente de orquestração durável para terminar os monitores. Pode alterar o intervalo de wait um monitor com base numa condição específica (por exemplo, backoff exponencial.)

O código seguinte implementa um monitor básico:

[FunctionName("MonitorJobStatus")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext 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.
}

Quando um pedido é recebido, é criada uma nova instância de orquestração para esse ID de tarefa. A instância consulta um estado até que uma condição seja cumprida ou até que um tempo limite expire. Um temporizador durável controla o intervalo de consulta. Em seguida, pode executar mais trabalho ou a orquestração pode terminar.

Padrão n.º 5: Interação humana

Muitos processos automatizados envolvem algum tipo de interação humana. Envolver humanos num processo automatizado é complicado porque as pessoas não são tão disponíveis e tão reativas como os serviços cloud. Um processo automatizado pode permitir esta interação através de tempos limite e lógica de compensação.

Um processo de aprovação é um exemplo de um processo de negócio que envolve interação humana. A aprovação de um gestor pode ser necessária para um relatório de despesas que exceda um determinado montante em dólares. Se o gestor não aprovar o relatório de despesas no prazo de 72 horas (talvez o gestor tenha ido de férias), um processo de escalamento inicia-se para obter a aprovação de outra pessoa (talvez do gestor do gestor).

Um diagrama do padrão de interação humana

Pode implementar o padrão neste exemplo com uma função de orquestrador. O orquestrador utiliza um temporizador durável para pedir aprovação. O orquestrador aumenta se ocorrer um tempo limite. O orquestrador aguarda por um evento externo, como uma notificação gerada por uma interação humana.

Estes exemplos criam um processo de aprovação para demonstrar o padrão de interação humana:

[FunctionName("ApprovalWorkflow")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    await context.CallActivityAsync("RequestApproval", null);
    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", null);
        }
    }
}

Para criar o temporizador durável, chame context.CreateTimer. A notificação é recebida por context.WaitForExternalEvent. Em seguida, Task.WhenAny é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).

Nota

Não é cobrado o tempo despendido à espera de eventos externos durante a execução no Plano de consumo.

Um cliente externo pode entregar a notificação de evento a uma função de orquestrador em espera com as APIs HTTP incorporadas:

curl -d "true" http://localhost:7071/runtime/webhooks/durabletask/instances/{instanceId}/raiseEvent/ApprovalEvent -H "Content-Type: application/json"

Um evento também pode ser gerado com o cliente de orquestração durável de outra função na mesma aplicação de funções:

[FunctionName("RaiseEventToOrchestration")]
public static async Task Run(
    [HttpTrigger] string instanceId,
    [DurableClient] IDurableOrchestrationClient client)
{
    bool isApproved = true;
    await client.RaiseEventAsync(instanceId, "ApprovalEvent", isApproved);
}

Padrão n.º 6: Agregador (entidades com estado)

O sexto padrão consiste em agregar dados de eventos ao longo de um período de tempo numa única entidade endereçável. Neste padrão, os dados que estão a ser agregados podem ser provenientes de várias origens, podem ser entregues em lotes ou podem estar dispersos por longos períodos de tempo. O agregador poderá ter de tomar medidas sobre os dados de eventos à medida que chegam e os clientes externos poderão ter de consultar os dados agregados.

Diagrama do agregador

O mais complicado de tentar implementar este padrão com funções normais e sem estado é que o controlo de simultaneidade se torna um grande desafio. Não só tem de se preocupar com a modificação de vários threads ao mesmo tempo, como também tem de se preocupar em garantir que o agregador só é executado numa única VM de cada vez.

Pode utilizar entidades Durable para implementar facilmente este padrão como uma única função.

[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
    int currentValue = ctx.GetState<int>();
    switch (ctx.OperationName.ToLowerInvariant())
    {
        case "add":
            int amount = ctx.GetInput<int>();
            ctx.SetState(currentValue + amount);
            break;
        case "reset":
            ctx.SetState(0);
            break;
        case "get":
            ctx.Return(currentValue);
            break;
    }
}

As entidades duráveis também podem ser modeladas como classes no .NET. Este modelo pode ser útil se a lista de operações for fixa e se tornar grande. O exemplo seguinte é uma implementação equivalente da Counter entidade com classes e métodos .NET.

public class Counter
{
    [JsonProperty("value")]
    public int CurrentValue { get; set; }

    public void Add(int amount) => this.CurrentValue += amount;

    public void Reset() => this.CurrentValue = 0;

    public int Get() => this.CurrentValue;

    [FunctionName(nameof(Counter))]
    public static Task Run([EntityTrigger] IDurableEntityContext ctx)
        => ctx.DispatchAsync<Counter>();
}

Os clientes podem colocar em fila de espera operações para (também conhecidas como "sinalização") uma função de entidade com o enlace do cliente da entidade.

Atualmente, as entidades duráveis não são suportadas no . Trabalho isolado da NET.

As funções de entidade estão disponíveis no Durable Functions 2.0 e superior para C#, JavaScript e Python.

A tecnologia

Nos bastidores, a extensão Durable Functions é criada sobre a Durable Task Framework, uma biblioteca open source no GitHub que é utilizada para criar fluxos de trabalho em código. Tal como Funções do Azure é a evolução sem servidor dos WebJobs do Azure, Durable Functions é a evolução sem servidor do Durable Task Framework. A Microsoft e outras organizações utilizam extensivamente o Durable Task Framework para automatizar processos críticos para a missão. É um ajuste natural para o ambiente de Funções do Azure sem servidor.

Restrições de código

Para fornecer garantias de execução fiáveis e de execução prolongada, as funções do orquestrador têm um conjunto de regras de codificação que têm de ser seguidas. Para obter mais informações, veja o artigo Orchestrator function code constraints (Restrições de código de função do Orchestrator ).

Faturação

Durable Functions são faturados da mesma forma que Funções do Azure. Para obter mais informações, veja Funções do Azure preços. Ao executar funções de orquestrador no plano de Consumo de Funções do Azure, existem alguns comportamentos de faturação a ter em conta. Para obter mais informações sobre estes comportamentos, consulte o artigo Durable Functions faturação.

Saltar para a direita

Pode começar a utilizar Durable Functions em menos de 10 minutos ao concluir um destes tutoriais de início rápido específicos do idioma:

Nestes inícios rápidos, vai criar e testar localmente uma função durável "hello world". Em seguida, publique o código de função no Azure. A função que criar orquestra e agrupar chama para outras funções.

Publicações

Durable Functions é desenvolvido em colaboração com a Microsoft Research. Como resultado, a equipa Durable Functions produz ativamente trabalhos de investigação e artefactos; estes incluem:

Saber mais

O vídeo seguinte destaca os benefícios do Durable Functions:

Para uma discussão mais aprofundada sobre Durable Functions e a tecnologia subjacente, veja o seguinte vídeo (está focado no .NET, mas os conceitos também se aplicam a outros idiomas suportados):

Uma vez que Durable Functions é uma extensão avançada para Funções do Azure, não é adequado para todas as aplicações. Para obter uma comparação com outras tecnologias de orquestração do Azure, veja Comparar Funções do Azure e o Azure Logic Apps.

Passos seguintes