Orquestrações duráveis

O Durable Functions é uma extensão do Azure Functions. Você pode usar uma função orchestrator para orquestrar a execução de outras funções duráveis dentro de um aplicativo de função. As funções de orquestrador têm as seguintes características:

  • As funções do Orchestrator definem fluxos de trabalho de funções usando código de procedimento. Não são necessários esquemas declarativos ou designers.
  • As funções do orquestrador podem chamar outras funções duráveis de forma síncrona e assíncrona. A saída de funções chamadas pode ser salva de forma confiável em variáveis locais.
  • As funções do orquestrador são duráveis e confiáveis. O progresso da execução é automaticamente verificado quando a função "aguarda" ou "rende". O estado local nunca é perdido quando o processo é reciclado ou a VM é reinicializada.
  • As funções do orquestrador podem ser de longa duração. A vida útil total de uma instância de orquestração pode ser de segundos, dias, meses ou interminável.

Este artigo fornece uma visão geral das funções do orquestrador e como elas podem ajudá-lo a resolver vários desafios de desenvolvimento de aplicativos. Se você ainda não estiver familiarizado com os tipos de funções disponíveis em um aplicativo Durable Functions, leia primeiro o artigo Durable Function types .

Identidade de orquestração

Cada instância de uma orquestração tem um identificador de instância (também conhecido como ID de instância). Por padrão, cada ID de instância é um GUID gerado automaticamente. No entanto, os IDs de instância também podem ser qualquer valor de cadeia de caracteres gerado pelo usuário. Cada ID de instância de orquestração deve ser exclusivo dentro de um hub de tarefas.

A seguir estão algumas regras sobre IDs de instância:

  • Os IDs de instância devem ter entre 1 e 100 caracteres.
  • Os IDs de instância não devem começar com @.
  • Os IDs de instância não devem conter /, \, #ou ? caracteres.
  • Os IDs de instância não devem conter caracteres de controle.

Nota

Geralmente, recomenda-se o uso de IDs de instância gerados automaticamente sempre que possível. Os IDs de instância gerados pelo usuário destinam-se a cenários em que há um mapeamento um-para-um entre uma instância de orquestração e alguma entidade externa específica do aplicativo, como uma ordem de compra ou um documento.

Além disso, a aplicação real de regras de restrição de caracteres pode variar dependendo do provedor de armazenamento que está sendo usado pelo aplicativo. Para garantir o comportamento correto e a compatibilidade, é altamente recomendável que você siga as regras de ID de instância listadas anteriormente.

O ID da instância de uma orquestração é um parâmetro necessário para a maioria das operações de gerenciamento de instâncias. Eles também são importantes para diagnósticos, como a pesquisa de dados de rastreamento de orquestração no Application Insights para fins de solução de problemas ou análise. Por esse motivo, é recomendável salvar os IDs de instância gerados em algum local externo (por exemplo, um banco de dados ou em logs de aplicativos) onde possam ser facilmente referenciados posteriormente.

Fiabilidade

As funções do Orchestrator mantêm de forma confiável seu estado de execução usando o padrão de design de fornecimento de eventos. Em vez de armazenar diretamente o estado atual de uma orquestração, o Durable Task Framework usa um armazenamento somente acréscimo para registrar a série completa de ações que a orquestração de funções executa. Um repositório somente apêndice tem muitos benefícios em comparação com "despejar" o estado de tempo de execução completo. Os benefícios incluem maior desempenho, escalabilidade e capacidade de resposta. Você também obtém consistência eventual para dados transacionais e trilhas e histórico de auditoria completos. As pistas de auditoria apoiam ações compensatórias fiáveis.

O Durable Functions usa o fornecimento de eventos de forma transparente. Nos bastidores, o await operador (C#) ou yield (JavaScript/Python) em uma função orchestrator produz o controle do thread do orchestrator de volta para o dispatcher do Durable Task Framework. No caso do Java, não existe uma palavra-chave de linguagem especial. Em vez disso, chamar .await() uma tarefa renderá o controle de volta ao dispatcher por meio de um arquivo .Throwable Em seguida, o dispatcher confirma todas as novas ações agendadas pela função orchestrator (como chamar uma ou mais funções filhas ou agendar um temporizador durável) para o armazenamento. A ação de confirmação transparente atualiza o histórico de execução da instância de orquestração anexando todos os novos eventos ao armazenamento, de forma muito semelhante a um log somente de acréscimo. Da mesma forma, a ação de confirmação cria mensagens no armazenamento para agendar o trabalho real. Neste ponto, a função orchestrator pode ser descarregada da memória. Por padrão, o Durable Functions usa o Armazenamento do Azure como seu armazenamento de estado de tempo de execução, mas outros provedores de armazenamento também são suportados.

Quando uma função de orquestração recebe mais trabalho para fazer (por exemplo, uma mensagem de resposta é recebida ou um temporizador durável expira), o orquestrador acorda e reexecuta toda a função desde o início para reconstruir o estado local. Durante a repetição, se o código tentar chamar uma função (ou fazer qualquer outro trabalho assíncrono), o Durable Task Framework consultará o histórico de execução da orquestração atual. Se ele achar que a função de atividade já foi executada e produziu um resultado, ele reproduz o resultado dessa função e o código do orquestrador continua a ser executado. A repetição continua até que o código da função seja concluído ou até que tenha agendado um novo trabalho assíncrono.

Nota

Para que o padrão de repetição funcione corretamente e de forma confiável, o código de função do orquestrador deve ser determinístico. O código do orquestrador não determinístico pode resultar em erros de tempo de execução ou outro comportamento inesperado. Para obter mais informações sobre restrições de código para funções do orchestrator, consulte a documentação de restrições de código de função do orchestrator.

Nota

Se uma função orchestrator emitir mensagens de log, o comportamento de repetição pode fazer com que mensagens de log duplicadas sejam emitidas. Consulte o tópico Registro em log para saber mais sobre por que esse comportamento ocorre e como contorná-lo.

História da orquestração

O comportamento de fornecimento de eventos do Durable Task Framework está intimamente ligado ao código de função do orquestrador que você escreve. Suponha que você tenha uma função de orquestrador de encadeamento de atividades, como a seguinte função de orquestrador:

Nota

A versão 4 do modelo de programação Node.js para o Azure Functions está disponível em geral. O novo modelo v4 foi projetado para ter uma experiência mais flexível e intuitiva para desenvolvedores de JavaScript e TypeScript. Saiba mais sobre as diferenças entre v3 e v4 no guia de migração.

Nos trechos de código a seguir, JavaScript (PM4) indica o modelo de programação V4, a nova experiência.

[FunctionName("HelloCities")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var outputs = new List<string>();

    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));

    // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
    return outputs;
}

Sempre que uma função de atividade é agendada, a Estrutura de Tarefa Durável verifica o estado de execução da função em algum back-end de armazenamento durável (armazenamento de Tabela do Azure por padrão). Este estado é o que é chamado de história da orquestração.

Tabela de histórico

De um modo geral, o Durable Task Framework faz o seguinte em cada ponto de verificação:

  1. Salva o histórico de execução em armazenamento durável.
  2. Enfileira mensagens para funções que o orquestrador deseja invocar.
  3. Enfileira mensagens para o próprio orquestrador — por exemplo, mensagens de temporizador duráveis.

Uma vez que o ponto de verificação esteja concluído, a função orchestrator fica livre para ser removida da memória até que haja mais trabalho para fazer.

Nota

O Armazenamento do Azure não fornece nenhuma garantia transacional entre salvar dados no armazenamento de tabelas e filas. Para lidar com falhas, o provedor de Armazenamento do Azure Durable Functions usa padrões de consistência eventuais. Esses padrões garantem que nenhum dado seja perdido se houver uma falha ou perda de conectividade no meio de um ponto de verificação. Provedores de armazenamento alternativos, como o provedor de armazenamento MSSQL Durable Functions, podem fornecer garantias de consistência mais fortes.

Após a conclusão, o histórico da função mostrada anteriormente se parece com a tabela a seguir no Armazenamento de Tabela do Azure (abreviada para fins de ilustração):

PartitionKey (InstanceId) EventType Carimbo de Data/Hora Entrada Nome Result Status
EAEE885B ExecuçãoIniciado 2021-05-05T18:45:28.852Z nulo OláCidades
EAEE885B OrchestratorIniciado 2021-05-05T18:45:32.362Z
EAEE885B TaskScheduled 2021-05-05T18:45:32.670Z SayHello
EAEE885B OrchestratorConcluído 2021-05-05T18:45:32.670Z
EAEE885B TarefaConcluída 2021-05-05T18:45:34.201Z ""Olá Tóquio!"""
EAEE885B OrchestratorIniciado 2021-05-05T18:45:34.232Z
EAEE885B TaskScheduled 2021-05-05T18:45:34.435Z SayHello
EAEE885B OrchestratorConcluído 2021-05-05T18:45:34.435Z
EAEE885B TarefaConcluída 2021-05-05T18:45:34.763Z ""Olá Seattle!"""
EAEE885B OrchestratorIniciado 2021-05-05T18:45:34.857Z
EAEE885B TaskScheduled 2021-05-05T18:45:34.857Z SayHello
EAEE885B OrchestratorConcluído 2021-05-05T18:45:34.857Z
EAEE885B TarefaConcluída 2021-05-05T18:45:34.919Z ""Olá Londres!"""
EAEE885B OrchestratorIniciado 2021-05-05T18:45:35.032Z
EAEE885B OrchestratorConcluído 2021-05-05T18:45:35.044Z
EAEE885B ExecuçãoConcluída 2021-05-05T18:45:35.044Z "["Olá Tóquio!"",""Olá Seattle!"","Olá Londres!""]" Concluído

Algumas notas sobre os valores da coluna:

  • PartitionKey: Contém o ID da instância da orquestração.
  • EventType: Representa o tipo do evento. Você pode encontrar descrições detalhadas de todos os tipos de eventos históricos aqui.
  • Carimbo de data/hora: O carimbo de data/hora UTC do evento histórico.
  • Nome: O nome da função que foi invocada.
  • Entrada: A entrada formatada em JSON da função.
  • Resultado: A saída da função, ou seja, o seu valor de retorno.

Aviso

Embora seja útil como uma ferramenta de depuração, não dependa desta tabela. Pode mudar à medida que a extensão Durable Functions evolui.

Toda vez que a função é retomada depois de aguardar a conclusão de uma tarefa, o Durable Task Framework executa novamente a função do orquestrador do zero. Em cada nova execução, ele consulta o histórico de execução para determinar se a tarefa assíncrona atual foi concluída. Se o histórico de execução mostrar que a tarefa já foi concluída, a estrutura reproduz a saída dessa tarefa e passa para a próxima tarefa. Este processo continua até que todo o histórico de execução tenha sido reproduzido. Uma vez que o histórico de execução atual tenha sido reproduzido, as variáveis locais terão sido restauradas para seus valores anteriores.

Características e padrões

As próximas seções descrevem os recursos e padrões das funções do orquestrador.

Sub-orquestrações

As funções do orquestrador podem chamar funções de atividade, mas também outras funções do orquestrador. Por exemplo, você pode criar uma orquestração maior a partir de uma biblioteca de funções de orquestrador. Ou, você pode executar várias instâncias de uma função orquestradora em paralelo.

Para obter mais informações e exemplos, consulte o artigo Suborquestrações .

Temporizadores duráveis

As orquestrações podem agendar temporizadores duráveis para implementar atrasos ou configurar a manipulação de tempo limite em ações assíncronas. Use temporizadores duráveis em funções de orquestrador em vez de APIs de "suspensão" nativas da linguagem.

Para obter mais informações e exemplos, consulte o artigo Temporizadores duráveis.

Eventos externos

As funções do Orchestrator podem aguardar eventos externos para atualizar uma instância de orquestração. Este recurso de funções duráveis geralmente é útil para lidar com uma interação humana ou outros retornos de chamada externos.

Para obter mais informações e exemplos, consulte o artigo Eventos externos.

Processamento de erros

As funções do Orchestrator podem usar os recursos de tratamento de erros da linguagem de programação. Padrões existentes como try/catch são suportados no código de orquestração.

As funções do orquestrador também podem adicionar políticas de repetição à atividade ou às funções do suborquestrador que elas chamam. Se uma atividade ou função de suborquestrador falhar com uma exceção, a política de repetição especificada pode atrasar e repetir automaticamente a execução até um número especificado de vezes.

Nota

Se houver uma exceção não tratada em uma função orchestrator, a instância de orquestração será concluída em um Failed estado. Uma instância de orquestração não pode ser repetida depois de falhar.

Para obter mais informações e exemplos, consulte o artigo Tratamento de erros.

Seções críticas (Durable Functions 2.x, atualmente somente .NET)

As instâncias de orquestração são single-threaded, portanto, não é necessário se preocupar com as condições de corrida dentro de uma orquestração. No entanto, as condições de corrida são possíveis quando as orquestrações interagem com sistemas externos. Para reduzir as condições de corrida ao interagir com sistemas externos, as funções do orquestrador podem definir seções críticas usando um LockAsync método no .NET.

O código de exemplo a seguir mostra uma função orchestrator que define uma seção crítica. Ele entra na seção crítica usando o LockAsync método. Esse método requer a passagem de uma ou mais referências para uma Entidade Durável, que gerencia de forma durável o estado de bloqueio. Apenas uma única instância dessa orquestração pode executar o código na seção crítica de cada vez.

[FunctionName("Synchronize")]
public static async Task Synchronize(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var lockId = new EntityId("LockEntity", "MyLockIdentifier");
    using (await context.LockAsync(lockId))
    {
        // critical section - only one orchestration can enter at a time
    }
}

O LockAsync adquire a(s) fechadura(s) durável(is) e retorna um IDisposable que termina a seção crítica quando descartado. Este IDisposable resultado pode ser usado em conjunto com um using bloco para obter uma representação sintática da seção crítica. Quando uma função orchestrator entra em uma seção crítica, apenas uma instância pode executar esse bloco de código. Quaisquer outras instâncias que tentem entrar na seção crítica serão bloqueadas até que a instância anterior saia da seção crítica.

O recurso de seção crítica também é útil para coordenar alterações em entidades duráveis. Para obter mais informações sobre seções críticas, consulte o tópico Entidades duráveis "Coordenação de entidades".

Nota

As secções críticas estão disponíveis em Durable Functions 2.0. Atualmente, apenas orquestrações .NET in-proc implementam esse recurso. Entidades e seções críticas ainda não estão disponíveis em Funções duráveis para trabalhador isolado por dotnet.

Chamando pontos de extremidade HTTP (Durable Functions 2.x)

As funções do orquestrador não têm permissão para fazer E/S, conforme descrito em Restrições de código de função do orquestrador. A solução típica para essa limitação é encapsular qualquer código que precise fazer E/S em uma função de atividade. As orquestrações que interagem com sistemas externos frequentemente usam funções de atividade para fazer chamadas HTTP e retornar o resultado para a orquestração.

Para simplificar esse padrão comum, as funções do orchestrator podem usar o CallHttpAsync método para invocar APIs HTTP diretamente.

[FunctionName("CheckSiteAvailable")]
public static async Task CheckSiteAvailable(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    Uri url = context.GetInput<Uri>();

    // Makes an HTTP GET request to the specified endpoint
    DurableHttpResponse response = 
        await context.CallHttpAsync(HttpMethod.Get, url);

    if ((int)response.StatusCode == 400)
    {
        // handling of error codes goes here
    }
}

Além de suportar padrões básicos de solicitação/resposta, o método suporta a manipulação automática de padrões comuns de sondagem assíncrona HTTP 202 e também suporta autenticação com serviços externos usando Identidades Gerenciadas.

Para obter mais informações e exemplos detalhados, consulte o artigo Recursos HTTP.

Nota

A chamada de pontos de extremidade HTTP diretamente das funções do orquestrador está disponível em Durable Functions 2.0 e superior.

Passando vários parâmetros

Não é possível passar vários parâmetros para uma função de atividade diretamente. A recomendação é passar uma matriz de objetos ou objetos compostos.

No .NET, você também pode usar objetos ValueTuple . O exemplo a seguir está usando novos recursos do ValueTuple adicionados com C# 7:

[FunctionName("GetCourseRecommendations")]
public static async Task<object> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string major = "ComputerScience";
    int universityYear = context.GetInput<int>();

    object courseRecommendations = await context.CallActivityAsync<object>(
        "CourseRecommendations",
        (major, universityYear));
    return courseRecommendations;
}

[FunctionName("CourseRecommendations")]
public static async Task<object> Mapper([ActivityTrigger] IDurableActivityContext inputs)
{
    // parse input for student's major and year in university
    (string Major, int UniversityYear) studentInfo = inputs.GetInput<(string, int)>();

    // retrieve and return course recommendations by major and university year
    return new
    {
        major = studentInfo.Major,
        universityYear = studentInfo.UniversityYear,
        recommendedCourses = new []
        {
            "Introduction to .NET Programming",
            "Introduction to Linux",
            "Becoming an Entrepreneur"
        }
    };
}

Próximos passos