Padrões de orquestraçãoOrchestration patterns

Durable Functions facilita a criação de fluxos de trabalho com estado que são compostos por atividades de longa execução discretas em um ambiente sem servidor.Durable Functions makes it easier to create stateful workflows that are composed of discrete, long running activities in a serverless environment. Como Durable Functions pode acompanhar o progresso de seus fluxos de trabalho e, periodicamente, fazer o ponto de verificação do histórico de execução, ele se presta à implementação de alguns padrões interessantes.Since Durable Functions can track the progress of your workflows and periodically checkpoints the execution history, it lends itself to implementing some interesting patterns.

Encadeamento de funçõesFunction chaining

Em um processo sequencial típico, as atividades precisam executar uma após a outra em uma ordem específica.In a typical sequential process, activities need to execute one after the other in a particular order. Opcionalmente, a atividade futura pode exigir alguma saída da função anterior.Optionally, the upcoming activity may require some output from the previous function. Essa dependência na ordenação de atividades cria uma cadeia de função de execução.This dependency on the ordering of activities creates a function chain of execution.

O benefício de usar Durable Functions para implementar esse padrão de fluxo de trabalho vem da sua capacidade de fazer o ponto de verificação.The benefit of using Durable Functions to implement this workflow pattern comes from its ability to do checkpointing. Se o servidor falhar, a rede atinge o tempo limite ou algum outro problema ocorre, as funções duráveis podem retomar o último estado conhecido e continuar executando o fluxo de trabalho mesmo se ele estiver em outro servidor.If the server crashes, the network times out or some other issue occurs, Durable functions can resume from the last known state and continue running your workflow even if it's on another server.

[FunctionName("PlaceOrder")]
public static async Task<string> PlaceOrder([OrchestrationTrigger] DurableOrchestrationContext context)
{
    OrderRequestData orderData = context.GetInput<OrderRequestData>();

    await context.CallActivityAsync<bool>("CheckAndReserveInventory", orderData);
    await context.CallActivityAsync<bool>("ProcessPayment", orderData);

    string trackingNumber = await context.CallActivityAsync<string>("ScheduleShipping", orderData);
    await context.CallActivityAsync<string>("EmailCustomer", trackingNumber);

    return trackingNumber;
}

No exemplo de código anterior, a CallActivityAsync função é responsável por executar uma determinada atividade em uma máquina virtual na data center.In the preceding code sample, the CallActivityAsync function is responsible for running a given activity on a virtual machine in the data center. Quando Await retornar e a tarefa subjacente for concluída, a execução será registrada na tabela de histórico.When the await returns and the underlying Task completes, the execution will be recorded to the history table. O código na função de orquestrador pode fazer uso de qualquer uma das construções familiares da biblioteca de tarefas paralelas e das palavras-chave Async/Await.The code in the orchestrator function can make use of any of the familiar constructs of the Task Parallel Library and the async/await keywords.

O código a seguir é um exemplo simplificado de ProcessPayment como o método pode ser:The following code is a simplified example of what the ProcessPayment method may look like:

[FunctionName("ProcessPayment")]
public static bool ProcessPayment([ActivityTrigger] DurableActivityContext context)
{
    OrderRequestData orderData = context.GetInput<OrderRequestData>();

    ApplyCoupons(orderData);
    if(IssuePaymentRequest(orderData)) {
        return true;
    }

    return false;
}

APIs HTTP assíncronasAsynchronous HTTP APIs

Em alguns casos, os fluxos de trabalho podem conter atividades que levam um período de tempo relativamente longo para ser concluído.In some cases, workflows may contain activities that take a relatively long period of time to complete. Imagine um processo que inicia o backup de arquivos de mídia no armazenamento de BLOBs.Imagine a process that kicks off the backup of media files into blob storage. Dependendo do tamanho e da quantidade dos arquivos de mídia, esse processo de backup pode levar horas para ser concluído.Depending on the size and quantity of the media files, this backup process may take hours to complete.

Nesse cenário, a DurableOrchestrationClient capacidade de verificar o status de um fluxo de trabalho em execução se torna útil.In this scenario, the DurableOrchestrationClient's ability to check the status of a running workflow becomes useful. Ao usar um HttpTrigger para iniciar um fluxo de trabalho, o CreateCheckStatusResponse método pode ser usado para retornar uma instância do HttpResponseMessage .When using an HttpTrigger to start a workflow, the CreateCheckStatusResponse method can be used to return an instance of HttpResponseMessage. Essa resposta fornece ao cliente um URI no conteúdo que pode ser usado para verificar o status do processo em execução.This response provides the client with a URI in the payload that can be used to check the status of the running process.

[FunctionName("OrderWorkflow")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, "POST")]HttpRequestMessage req,
    [OrchestrationClient ] DurableOrchestrationClient orchestrationClient)
{
    OrderRequestData data = await req.Content.ReadAsAsync<OrderRequestData>();

    string instanceId = await orchestrationClient.StartNewAsync("PlaceOrder", data);

    return orchestrationClient.CreateCheckStatusResponse(req, instanceId);
}

O resultado de exemplo abaixo mostra a estrutura da carga de resposta.The sample result below shows the structure of the response payload.

{
    "id": "instanceId",
    "statusQueryGetUri": "http://host/statusUri",
    "sendEventPostUri": "http://host/eventUri",
    "terminatePostUri": "http://host/terminateUri"
}

Usando seu cliente HTTP preferencial, as solicitações GET podem ser feitas para o URI no statusQueryGetUri para inspecionar o status do fluxo de trabalho em execução.Using your preferred HTTP client, GET requests can be made to the URI in statusQueryGetUri to inspect the status of the running workflow. A resposta de status retornada deve ser semelhante ao código a seguir.The returned status response should resemble the following code.

{
    "runtimeStatus": "Running",
    "input": {
        "$type": "DurableFunctionsDemos.OrderRequestData, DurableFunctionsDemos"
    },
    "output": null,
    "createdTime": "2018-01-01T00:22:05Z",
    "lastUpdatedTime": "2018-01-01T00:22:09Z"
}

Conforme o processo continua, a resposta de status será alterada para com falha ou concluída.As the process continues, the status response will change to either Failed or Completed. Após a conclusão bem-sucedida, a propriedade de saída na carga conterá todos os dados retornados.On successful completion, the output property in the payload will contain any returned data.

MonitoramentoMonitoring

Para tarefas recorrentes simples, Azure Functions fornece o TimerTrigger que pode ser agendado com base em uma expressão cron.For simple recurring tasks, Azure Functions provides the TimerTrigger that can be scheduled based on a CRON expression. O temporizador funciona bem para tarefas simples e de curta duração, mas pode haver cenários onde é necessário um agendamento mais flexível.The timer works well for simple, short-lived tasks, but there might be scenarios where more flexible scheduling is needed. Esse cenário é quando o padrão de monitoramento e o Durable Functions podem ajudar.This scenario is when the monitoring pattern and Durable Functions can help.

Durable Functions permite intervalos de agendamento flexíveis, gerenciamento de tempo de vida e a criação de vários processos de monitor de uma única função de orquestração.Durable Functions allows for flexible scheduling intervals, lifetime management, and the creation of multiple monitor processes from a single orchestration function. Um caso de uso para essa funcionalidade pode ser criar observadores para alterações de preço de ações que são concluídas quando um determinado limite é atingido.One use case for this functionality might be to create watchers for stock price changes that complete once a certain threshold is met.

[FunctionName("CheckStockPrice")]
public static async Task CheckStockPrice([OrchestrationTrigger] DurableOrchestrationContext context)
{
    StockWatcherInfo stockInfo = context.GetInput<StockWatcherInfo>();
    const int checkIntervalSeconds = 120;
    StockPrice initialStockPrice = null;

    DateTime fireAt;
    DateTime exitTime = context.CurrentUtcDateTime.Add(stockInfo.TimeLimit);

    while (context.CurrentUtcDateTime < exitTime)
    {
        StockPrice currentStockPrice = await context.CallActivityAsync<StockPrice>("GetStockPrice", stockInfo);

        if (initialStockPrice == null)
        {
            initialStockPrice = currentStockPrice;
            fireAt = context.CurrentUtcDateTime.AddSeconds(checkIntervalSeconds);
            await context.CreateTimer(fireAt, CancellationToken.None);
            continue;
        }

        decimal percentageChange = (initialStockPrice.Price - currentStockPrice.Price) /
                               ((initialStockPrice.Price + currentStockPrice.Price) / 2);

        if (Math.Abs(percentageChange) >= stockInfo.PercentageChange)
        {
            // Change threshold detected
            await context.CallActivityAsync("NotifyStockPercentageChange", currentStockPrice);
            break;
        }

        // Sleep til next polling interval
        fireAt = context.CurrentUtcDateTime.AddSeconds(checkIntervalSeconds);
        await context.CreateTimer(fireAt, CancellationToken.None);
    }
}

DurableOrchestrationContext``CreateTimero método do configura o agendamento para a próxima invocação do loop para verificar as alterações de preço de ações.DurableOrchestrationContext's CreateTimer method sets up the schedule for the next invocation of the loop to check for stock price changes. DurableOrchestrationContext também tem uma CurrentUtcDateTime propriedade para obter o valor DateTime atual em UTC.DurableOrchestrationContext also has a CurrentUtcDateTime property to get the current DateTime value in UTC. É melhor usar essa propriedade em vez de DateTime.UtcNow porque ela é facilmente simulada para teste.It's better to use this property instead of DateTime.UtcNow because it's easily mocked for testing.