O que é a Durable Functions?

Funções Duradouras é uma extensão de Funções Azure que permite escrever funções imponentes num ambiente de computação sem servidor. A extensão permite definir fluxos de trabalho imponentes escrevendo funções orquestradoras e entidades imponentes, escrevendo funções de entidade utilizando o modelo de programação Azure Functions. Nos bastidores, a extensão gere o estado, os postos de controlo e o recomeço para si, permitindo-lhe focar-se na sua lógica de negócio.

Linguagens suportadas

Funções Duradouras suportam atualmente os seguintes idiomas:

  • C.: ambas as bibliotecas de classes pré-recompensadas e o script C#.
  • JavaScript: suportado apenas para a versão 2.x do tempo de execução das Funções Azure. Requer a versão 1.7.0 da extensão funções duradouras, ou uma versão posterior.
  • Python: requer a versão 2.3.1 da extensão de Funções Duráveis, ou uma versão posterior.
  • F#: bibliotecas de classes pré-recompensadas e script F#. F# O script só é suportado para a versão 1.x do tempo de execução das Funções Azure.
  • PowerShell: Suportado apenas para a versão 3.x do tempo de funcionamento das funções Azure e PowerShell 7. Requer a versão 2.x das extensões do pacote.

Para aceder às funcionalidades e atualizações mais recentes, recomenda-se que utilize as versões mais recentes da extensão de Funções Duradouras e as bibliotecas de Funções Duráveis específicas da linguagem. Saiba mais sobre as versões Funções Duradouras.

As Funções Duradouras têm como objetivo apoiar todas as línguas Azure Functions. Consulte a lista de problemas de funções duradouras para obter o mais recente estado de trabalho para suportar idiomas adicionais.

Tal como as Funções Azure, existem modelos para o ajudar a desenvolver Funções Duráveis utilizando o Visual Studio 2019, o Visual Studio Codee o portal Azure.

Padrões de aplicação

O caso de utilização primária para funções duradouras está a simplificar requisitos complexos e apátridas de coordenação em aplicações sem servidor. As seguintes secções descrevem padrões típicos de aplicação que podem beneficiar de Funções Duradouras:

Padrão #1: Acorrentamento de funções

No padrão de acorrentamento da função, uma sequência de funções executa numa ordem específica. Neste padrão, a saída de uma função é aplicada à entrada de outra função.

Um diagrama do padrão de acorrentamento da função

Pode utilizar funções duradouras para implementar o padrão de corrente de função de forma concisa, como mostrado no exemplo seguinte.

Neste exemplo, os valores F1 , , e são os F2 F3 F4 nomes de outras funções na mesma aplicação de função. Pode implementar o fluxo de controlo utilizando construções normais de codificação imperativas. O código executa de cima para baixo. O código pode envolver a semântica de fluxo de fluxo de linguagem existente, como condicional e loops. Pode incluir lógica de manuseamento de erros em try / catch / finally 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 pelo nome, passar parâmetros e devolver a saída da função. De cada vez que o código await chama, o quadro de funções duráveis verifica o progresso da instância de função atual. Se o processo ou máquina virtual reciclar a meio da execução, a instância de função retoma a partir da await chamada anterior. Para mais informações, consulte a secção seguinte, Padrão #2: Ventilador para fora/ventilador dentro

Padrão #2: Ventilador para fora/ventilador em

No ventilador out/ventilador em padrão, executa várias funções em paralelo e, em seguida, aguarde que todas as funções terminem. Muitas vezes, alguns trabalhos de agregação são feitos sobre os resultados que são devolvidos das funções.

Um diagrama do ventilador fora/padrão de ventilador

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

A extensão funções duráveis lida com 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 ventilação é distribuído em várias instâncias da F2 função. O trabalho é acompanhado através de uma lista dinâmica de tarefas. Task.WhenAll é chamado a esperar que todas as funções chamadas terminem. Em seguida, as F2 saídas de função são agregadas da lista de tarefas dinâmicas e transmitidas para a F3 função.

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

Nota

Em circunstâncias raras, é possível que um acidente possa acontecer na janela depois de uma função de atividade terminar, mas antes que a sua conclusão seja guardada para a história da orquestração. Se isso acontecer, a função de atividade seria re-executada desde o início após a recuperação do processo.

Padrão #3: APIs HTTP Async

O padrão da API HTTP async aborda o problema da coordenação do estado das operações de longa duração com clientes externos. Uma forma comum de implementar este padrão é tendo um ponto final HTTP desencadeando a ação de longa duração. Em seguida, redirecione o cliente para um ponto final de estado que o cliente sonda para saber quando a operação está terminada.

Um diagrama do padrão http API

As Funções Duradouras fornecem suporte incorporado para este padrão, simplificando ou mesmo removendo o código que precisa de escrever para interagir com execuções de função de longa duração. Por exemplo, as amostras de arranque rápido de Funções Durable(C# e JavaScript) mostram um simples comando REST que pode utilizar para iniciar novas instâncias de função de orquestrador. Após o início de um caso, a extensão expõe o webhook HTTP APIs que consulta o estado da função do orquestrador.

O exemplo a seguir mostra comandos REST que iniciam um orquestrador e consultam o seu 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", ...}

Como o tempo de funcionamento das Funções Duradouras gere o seu estado, não precisa de implementar o seu próprio mecanismo de rastreio de estado.

A extensão Funções Duradouras expõe APIs HTTP incorporadas que gerem orquestrações de longa duração. Em alternativa, pode implementar este padrão utilizando os seus próprios gatilhos de função (como HTTP, uma fila ou Azure Event Hubs) e a ligação do cliente de orquestração. Por exemplo, pode utilizar uma mensagem de fila para desencadear a rescisão. Ou, pode utilizar um gatilho HTTP protegido por uma política de autenticação do Azure Ative Directory em vez das APIs HTTP incorporadas que utilizam uma chave gerada para a autenticação.

Para obter mais informações, consulte o artigo de funcionalidades HTTP, que explica como pode expor processos assíncronos e de longa duração sobre HTTP utilizando a extensão de Funções Duradouras.

Padrão #4: Monitor

O padrão do monitor refere-se a um processo flexível e recorrente num fluxo de trabalho. Um exemplo é a sondagem até que as condições específicas sejam satisfeitas. Pode utilizar um gatilho de temporizador regular para abordar um cenário básico, como um trabalho de limpeza periódica, mas o seu intervalo é estático e gerir as vidas de instâncias torna-se complexo. Pode utilizar funções duradouras para criar intervalos de recorrência flexíveis, gerir as tarefas e criar múltiplos processos de monitorização a partir de uma única orquestração.

Um exemplo do padrão do monitor é inverter o cenário anterior da API HTTP HTTP. Em vez de expor um ponto final para um cliente externo monitorizar uma operação de longa duração, o monitor de longa duração consome um ponto final externo e, em seguida, aguarda por uma mudança de estado.

Um diagrama do padrão do monitor

Em algumas linhas de código, pode utilizar Funções Duráveis para criar múltiplos monitores que observam pontos finais arbitrários. Os monitores podem terminar a execução quando uma condição é satisfeita, ou outra função pode usar o cliente de orquestração durável para terminar os monitores. Pode alterar o intervalo de um monitor wait com base numa condição específica (por exemplo, recuo exponencial.)

O seguinte código 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, uma nova instância de orquestração é criada para esse iD de trabalho. O caso sonda um estado até que uma condição seja satisfeita e o loop seja saído. Um temporizador durável controla o intervalo de votação. Então, mais trabalho pode ser realizado, ou a orquestração pode terminar. Quando nextCheck expiryTime exceder, o monitor termina.

Padrão #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 estão tão disponíveis e tão responsivas como serviços na nuvem. Um processo automatizado pode permitir esta interação utilizando intervalos de tempo 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 valor em dólares. Se o gestor não aprovar o relatório de despesas dentro de 72 horas (talvez o gerente tenha ido de férias), um processo de escalada começa a receber a aprovação de outra pessoa (talvez o gerente).

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

Pode implementar o padrão neste exemplo utilizando uma função orquestradora. O orquestrador usa um temporizador durável para solicitar aprovação. O orquestrador aumenta se ocorrer tempo. O orquestrador aguarda um evento externo, como uma notificação que é 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, context.CreateTimer ligue. A notificação é recebida por context.WaitForExternalEvent . Em seguida, Task.WhenAny é chamado a decidir se se agrava (o intervalo acontece primeiro) ou processa a aprovação (a aprovação é recebida antes do intervalo).

Um cliente externo pode entregar a notificação do evento a uma função de orquestrador de espera utilizando 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 levantado usando o cliente de orquestração durável a partir de outra função na mesma aplicação de função:

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

Padrão #6: Agregador (entidades imponentes)

O sexto padrão é sobre agregar dados de eventos durante um período de tempo numa única entidade endereçada. Neste padrão, os dados agregados podem vir de múltiplas fontes, podem ser entregues em lotes, ou podem ser dispersos por longos períodos de tempo. O agregador poderá ter de tomar medidas sobre os dados dos eventos à medida que chega, e os clientes externos podem precisar de consultar os dados agregados.

Diagrama agregador

O mais complicado de tentar implementar este padrão com funções normais e apátridas é que o controlo da conuncy torna-se um grande desafio. Não só precisa de se preocupar com vários fios que modificam os mesmos dados ao mesmo tempo, como também tem de se preocupar em garantir que o agregador só funciona num único VM de cada vez.

Você pode usar entidades duráveis 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;
    }
}

Entidades duradouras também podem ser modeladas como classes em .NET. Este modelo pode ser útil se a lista de operações for fixa e se tornar grande. O exemplo a seguir é uma implementação equivalente da Counter entidade utilizando 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 enquadrizar operações para (também conhecida como "sinalização") uma função de entidade utilizando a ligação do cliente da entidade.

[FunctionName("EventHubTriggerCSharp")]
public static async Task Run(
    [EventHubTrigger("device-sensor-events")] EventData eventData,
    [DurableClient] IDurableEntityClient 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);
}

Nota

Os proxies gerados dinamicamente também estão disponíveis em .NET para entidades de sinalização de forma tipo segura. Além da sinalização, os clientes também podem consultar o estado de uma função de entidade utilizando métodos de tipo seguro na ligação do cliente de orquestração.

As funções de entidade estão disponíveis em Funções Duráveis 2.0 e acima para C#, JavaScript e Python.

A tecnologia

Nos bastidores, a extensão de Funções Duradouras é construída em cima do Quadro de Tarefas Durable, uma biblioteca de código aberto no GitHub que é usada para construir fluxos de trabalho em código. Tal como as Funções Azure é a evolução sem servidor do Azure WebJobs, as Funções Duradouras são a evolução sem servidor do Quadro de Tarefas Duráveis. A Microsoft e outras organizações utilizam o Quadro de Tarefas Durable extensivamente para automatizar processos críticos da missão. É um ajuste natural para o ambiente Azure Functions sem servidor.

Restrições de código

A fim de fornecer garantias de execução fiáveis e de longa duração, as funções de orquestrador têm um conjunto de regras de codificação que devem ser seguidas. Para obter mais informações, consulte o artigo de restrições de função do Orquestrador.

Faturação

As funções duradouras são faturadas da mesma forma que as Funções Azure. Para obter mais informações, consulte os preços do Azure Functions. Ao executar funções de orquestrador no planode consumo de funções Azure, existem alguns comportamentos de faturação a ter em conta. Para obter mais informações sobre estes comportamentos, consulte o artigo de faturação de Funções Duradouras.

Salta para dentro

Você pode começar com Funções Duráveis em menos de 10 minutos completando um destes tutoriais de arranque rápido específicos da linguagem:

Nestes arranques rápidos, você cria localmente e testa uma função durável "olá mundo". Em seguida, publique o código de função no Azure. A função que cria orquestra e acorrenta em conjunto chama a outras funções.

Saber mais

O seguinte vídeo destaca os benefícios das Funções Duradouras:

Para uma discussão mais aprofundada sobre as Funções Duradouras e a tecnologia subjacente, consulte o seguinte vídeo (está focado em .NET, mas os conceitos também se aplicam a outras línguas apoiadas):

Como as Funções Duradouras são uma extensão avançada para funções Azure,não é apropriado para todas as aplicações. Para uma comparação com outras tecnologias de orquestração Azure, consulte Compare Azure Functions e Azure Logic Apps.

Passos seguintes