Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Desenvolve aplicações com estado utilizando Durable Functions. É uma extensão de Azure Functions. Usa uma função orchestrator para coordenar outras Funções Duráveis na sua aplicação de funções. As funções do Orchestrator são com estado, fiáveis e foram feitas para funcionar durante muito tempo.
Construa fluxos de trabalho com estado e tolerantes a falhas com os SDKs de Tarefas Duráveis em .NET, Python e Java. Use um orquestrador para coordenar atividades e suborquestrações. Os orquestradores são com estado, fiáveis e foram feitos para funcionar durante muito tempo.
Restrições do código 'Orchestrator'
As funções do Orchestrator utilizam event sourcing para garantir uma execução fiável e manter o estado das variáveis locais. O comportamento de repetição do código do orquestrador cria restrições quanto ao tipo de código que pode escrever numa função do orquestrador. Por exemplo, as funções orquestradoras devem ser determinísticas: uma função orquestradora repete-se várias vezes e deve produzir o mesmo resultado em cada ocasião.
Os orquestradores utilizam event sourcing para garantir uma execução fiável e manter o estado da variável local. O comportamento de repetição do código do orquestrador cria restrições quanto ao tipo de código que pode escrever num orquestrador. Por exemplo, os orquestradores devem ser determinísticos: um orquestrador repete várias vezes e deve produzir o mesmo resultado em cada vez.
Utilizar APIs determinísticas
Aqui estão algumas orientações simples para ajudar a garantir que o seu código é determinístico.
Chame APIs das suas línguas de destino em funções de orquestrador, mas use apenas APIs determinísticas. Uma API determinística devolve sempre o mesmo valor para a mesma entrada, independentemente de quando ou com que frequência é chamada.
As secções seguintes fornecem orientações sobre APIs e padrões que deve evitar porque não são determinísticos. Estas restrições aplicam-se apenas às funções do orquestrador. Outros tipos de funções não têm tais restrições.
Aqui estão algumas orientações simples para ajudar a garantir que o seu código é determinístico.
Chama APIs das tuas línguas de destino em orquestradores, mas usa apenas APIs determinísticas. Uma API determinística devolve sempre o mesmo valor para a mesma entrada, independentemente de quando ou com que frequência é chamada.
As secções seguintes fornecem orientações sobre APIs e padrões que deve evitar porque não são determinísticos. Estas restrições aplicam-se apenas a orquestradores. As atividades não têm essas restrições.
Observação
Este artigo aborda as restrições comuns do código do Orchestrator, mas não é abrangente. Foque-se em saber se uma API é determinística. Com essa mentalidade, normalmente consegues perceber quais as APIs que são seguras de usar sem recorrer a esta lista.
Datas e horários
As APIs baseadas em tempo são não deterministas e nunca devem ser usadas em funções de orquestrador. Cada repetição de função do orquestrador produz um valor diferente. Em vez disso, use a API equivalente ao Durable Functions para obter a data ou hora atual, que se mantém consistente entre as repetições.
Não uses DateTime.Now, DateTime.UtcNow, ou APIs equivalentes para obter a hora atual. Aulas como Stopwatch também devem ser evitadas. Para funções orquestradoras em processo do .NET, use a propriedade IDurableOrchestrationContext.CurrentUtcDateTime para obter o tempo atual. Para .NET funções de orquestrador isoladas, use a propriedade TaskOrchestrationContext.CurrentDateTimeUtc para obter o tempo atual.
DateTime startTime = context.CurrentUtcDateTime;
// do some work
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);
APIs baseadas no tempo são não determinísticas e nunca devem ser usadas em orquestradores. Cada repetição do orquestrador produz um valor diferente. Em vez disso, utilize a API equivalente ao SDK de Tarefas Duráveis para obter a data ou hora atual, que se mantém consistente entre as repetições.
Não uses DateTime.Now, DateTime.UtcNow, ou APIs equivalentes para obter a hora atual. Classes como Stopwatch também devem ser evitadas. Usa a TaskOrchestrationContext.CurrentUtcDateTime propriedade para obter a hora atual.
using Microsoft.DurableTask;
public class TimerExample : TaskOrchestrator<object?, TimeSpan>
{
public override async Task<TimeSpan> RunAsync(TaskOrchestrationContext context, object? input)
{
// Use context.CurrentUtcDateTime instead of DateTime.Now or DateTime.UtcNow
DateTime startTime = context.CurrentUtcDateTime;
// do some work
await context.CallActivityAsync("DoWork", null);
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);
return totalTime;
}
}
GUIDs e UUIDs
APIs que devolvem um GUID ou UUID aleatório são não determinísticas porque o valor gerado é diferente em cada repetição. Dependendo da sua linguagem, pode estar disponível uma API incorporada para gerar GUIDs determinísticos ou UUIDs. Caso contrário, use uma função de atividade para devolver um GUID ou UUID gerado aleatoriamente.
Em vez de APIs como Guid.NewGuid(), usa a API do NewGuid() objeto de contexto para gerar um GUID aleatório que seja seguro para repetição no Orchestrator.
Guid randomGuid = context.NewGuid();
Observação
Os GUIDs gerados com APIs de contexto de orquestração são UUIDs do Tipo 5.
APIs que devolvem um GUID ou UUID aleatório são não determinísticas porque o valor gerado é diferente em cada repetição. Dependendo da sua linguagem, pode estar disponível uma API incorporada para gerar GUIDs determinísticos ou UUIDs. Caso contrário, use uma atividade para devolver um GUID ou UUID gerado aleatoriamente.
Em vez de APIs como Guid.NewGuid(), usa a API do NewGuid() objeto de contexto para gerar um GUID aleatório que seja seguro para repetição no Orchestrator.
using Microsoft.DurableTask;
public class GuidExample : TaskOrchestrator<object?, Guid>
{
public override async Task<Guid> RunAsync(TaskOrchestrationContext context, object? input)
{
// Use context.NewGuid() instead of Guid.NewGuid()
Guid randomGuid = context.NewGuid();
return randomGuid;
}
}
Observação
Os GUIDs gerados com APIs de contexto de orquestração são UUIDs do Tipo 5.
Números aleatórios
Use uma função de atividade para devolver números aleatórios a uma função orquestradora. Os valores de retorno das funções de atividade são sempre seguros para reexecução porque estão guardados no histórico de orquestração.
Em alternativa, pode usar um gerador de números aleatórios com um valor de semente fixo diretamente numa função de orquestrador. Esta abordagem é segura desde que a mesma sequência de números seja gerada para cada repetição de orquestração.
Usa uma atividade para devolver números aleatórios a um orquestrador. Os valores de retorno das atividades são sempre seguros para repetição porque são guardados no histórico de orquestração.
Alternativamente, pode usar um gerador de números aleatórios com um valor de semente fixo diretamente num orquestrador. Esta abordagem é segura desde que a mesma sequência de números seja gerada para cada repetição de orquestração.
Vinculações
Não use bindings numa função de orquestrador, incluindo as associações do cliente de orquestração e do cliente de entidade. Use ligações de entrada e saída apenas numa função cliente ou de atividade. As funções do orquestrador podem ser reexecutadas várias vezes, provocando operações de E/S não determinísticas e duplicadas com sistemas externos.
Os orquestradores não devem realizar operações diretas de I/O com sistemas externos. Transferir operações de I/O para atividades. Os orquestradores podem reexecutar várias vezes, causando entradas e saídas duplicadas e não determinísticas com sistemas externos.
Variáveis estáticas
As variáveis estáticas podem mudar ao longo do tempo, tornando-as inseguras para funções de orquestrador. Evite usar variáveis estáticas em funções de orquestrador porque os seus valores podem mudar ao longo do tempo, resultando num comportamento de execução não determinista. Em vez disso, use constantes ou limite o uso de variáveis estáticas a funções de atividade.
As variáveis estáticas podem mudar ao longo do tempo, tornando-as inseguras para os orquestradores. Evite usar variáveis estáticas em orquestradores porque os seus valores podem mudar ao longo do tempo, resultando num comportamento de execução não determinístico. Em vez disso, use constantes ou limite o uso de variáveis estáticas a atividades.
Observação
Mesmo fora das funções de orquestrador, usar variáveis estáticas no Azure Functions pode ser problemático por várias razões, pois não há garantia de que o estado estático persista em várias execuções de funções. Evite variáveis estáticas, exceto em casos de uso específicos, como melhor esforço de cache em memória em funções de atividade ou entidade.
Variáveis de ambiente
As variáveis de ambiente nas funções do orquestrador podem mudar ao longo do tempo, resultando num comportamento de execução não determinístico. Se uma função orquestrador precisar de configuração definida numa variável de ambiente, deve passar o valor de configuração para a função orquestrador como entrada ou como valor de retorno de uma função de atividade.
As variáveis de ambiente nos orquestradores podem mudar ao longo do tempo, resultando num comportamento de execução não determinístico. Se um orquestrador precisar de configuração definida numa variável de ambiente, deve passar o valor de configuração para o orquestrador como entrada ou como valor de retorno de uma atividade.
Rede e HTTP
Use funções de atividade para efetuar chamadas de rede de saída. Se precisar de fazer uma chamada HTTP a partir da função do seu orquestrador, também pode usar as APIs HTTP duráveis.
Use atividades para fazer chamadas de rede de saída. Os orquestradores nunca devem fazer chamadas HTTP diretas ou outros pedidos de rede porque estas operações são não determinísticas.
APIs de bloqueio de threads
APIs bloqueadoras como sleep podem causar problemas de desempenho e escalabilidade nas funções do orquestrador e podem resultar em custos desnecessários de tempo de execução no plano de consumo do Azure Functions. Use alternativas quando disponíveis. Por exemplo, utilize temporizadores duráveis para criar atrasos que são seguros para reprodução e que não contam para o tempo de execução do orquestrador.
Bloquear APIs como o "sleep" pode causar problemas de desempenho e escala para os orquestradores e deve ser evitado. Use temporizadores duráveis para criar atrasos seguros para repetição.
Use context.CreateTimer() em vez de Task.Delay() ou Thread.Sleep().
// Don't use Task.Delay() or Thread.Sleep()
// Use context.CreateTimer() instead
await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None);
APIs assíncronas
O código Orchestrator nunca deve iniciar qualquer operação assíncrona, exceto operações definidas pelo objeto de contexto do trigger de orquestração. Por exemplo, nunca uses Task.Run, Task.Delay e HttpClient.SendAsync em .NET ou setTimeout e setInterval em JavaScript. Uma função de orquestrador deve apenas agendar trabalho assíncrono usando APIs de SDK Duráveis, como funções de atividade de agendamento. Qualquer outro tipo de invocação assíncrona deve ser feita dentro das funções de atividade.
O código do Orchestrator nunca deve iniciar qualquer operação assíncrona, exceto operações definidas pelo objeto de contexto de orquestração. Por exemplo, nunca use Task.Run, Task.Delay e HttpClient.SendAsync em .NET. Um orquestrador deve apenas agendar trabalho assíncrono usando APIs do SDK de Tarefas Duradouras, como atividades de agendamento. Qualquer outro tipo de invocação assíncrona deve ser feita dentro das atividades.
Funções JavaScript assíncronas
Declare as funções do orquestrador JavaScript como funções geradoras síncronas. Não declare funções orquestradoras em JavaScript, porque o runtime do Node.js não garante comportamento determinístico para essas funções.
Corutinas Python
Não declare as funções orquestradoras do Python como corrotinas. Não use a palavra-chave async porque a semântica das coroutines não está alinhada com o modelo de repetição das Durable Functions. Declare funções de orquestrador Python como geradores e use yield em vez de await com a API context.
Não deve declarar orquestradores em Python como corrotinas. Ou seja, nunca declare orquestradores em Python com a palavra-chave async porque a semântica das corrotinas não está alinhada com o modelo de repetição do Durable Task. Deves sempre declarar Python orchestrators como geradores, o que significa que deves usar yield em vez de await ao chamar APIs de contexto.
from durabletask import task
# CORRECT - use yield (generator function)
def my_orchestrator(ctx: task.OrchestrationContext, input: str):
result = yield ctx.call_activity(my_activity, input=input)
return result
# WRONG - don't use async/await
async def bad_orchestrator(ctx: task.OrchestrationContext, input: str):
result = await ctx.call_activity(my_activity, input=input) # This won't work!
return result
APIs de threads .NET
O Durable Task Framework executa código orquestrador num único thread e não pode interagir com outros threads. Executar continuações assíncronas numa thread de pool de trabalhadores na execução de uma orquestração pode resultar em execuções não determinísticas ou bloqueios. Por esta razão, as funções do teu orquestrador quase nunca devem usar APIs de threading. Por exemplo, nunca use ConfigureAwait(continueOnCapturedContext: false) numa função de orquestrador para garantir que as continuações de tarefas correm na função original SynchronizationContext do orquestrador.
Observação
O Durable Task Framework tenta detetar o uso acidental de threads não orquestradores em funções de orquestrador. Se encontrar uma violação, o framework lança uma exceção NonDeterministicOrchestrationException . No entanto, este comportamento de deteção não vai detetar todas as infrações, e não deve depender disso.
O Durable Task Framework executa código orquestrador num único thread e não pode interagir com outros threads. Executar continuações assíncronas numa thread de pool de trabalhadores na execução de uma orquestração pode resultar em execuções não determinísticas ou bloqueios. Por esta razão, os orquestradores quase nunca devem usar APIs de threading. Por exemplo, nunca use ConfigureAwait(continueOnCapturedContext: false) num orquestrador para garantir que as continuações da tarefa correm na versão original SynchronizationContextdo orquestrador .
Observação
O Durable Task Framework tenta detetar o uso acidental de threads não-orquestradores em orquestradores. Se encontrar uma violação, o framework lança uma exceção NonDeterministicOrchestrationException . No entanto, este comportamento de deteção não vai detetar todas as infrações, e não deve depender disso.
Versioning
Uma orquestração duradoura pode durar dias, meses, anos ou até como uma orquestração eterna. Alterações ao código que afetam orquestrações em execução podem quebrar o comportamento de repetição, por isso planeie cuidadosamente antes de atualizar a sua aplicação. Para mais informações, consulte Versionamento.
Uma orquestração duradoura pode durar dias, meses, anos ou até indefinidamente. Alterações ao código que afetam orquestrações em execução podem quebrar o comportamento de repetição, por isso planeie cuidadosamente antes de atualizar a sua aplicação. Estratégias comuns de versionamento incluem a implantação lado a lado e a utilização de nomes de hubs de tarefas específicos da versão.
Tarefas duradouras
Observação
Esta secção descreve detalhes internos de implementação do Quadro de Tarefas Duradouras. Não precisas de saber esta informação para usar as Durable Functions, mas ajuda a explicar o comportamento de repetição.
Tarefas que podem esperar em segurança nas funções de orquestrador são, por vezes, chamadas de tarefas duráveis. O Durable Task Framework cria e gere estas tarefas. Exemplos incluem as tarefas devolvidas por CallActivityAsync, WaitForExternalEvent e CreateTimer nas funções .NET orquestrador.
Uma lista de objetos TaskCompletionSource em .NET gere internamente estas tarefas duradouras. Durante a reprodução, o código do orquestrador cria estas tarefas. O despachante completa-os à medida que enumera os eventos históricos correspondentes.
O runtime executa as tarefas de forma síncrona num único thread até que o histórico seja reproduzido. Se uma tarefa durável não terminar até ao final da reprodução do histórico, o ambiente de execução toma as ações apropriadas. Por exemplo, o runtime pode enfileirar uma mensagem para chamar uma função de atividade.
Este comportamento em tempo de execução explica porque é que a tua função de orquestrador não consegue usar await ou yield numa tarefa não durável. O thread do dispatcher não pode esperar que a tarefa termine, e os callbacks dessa tarefa podem corromper o estado de rastreamento da função do orquestrador. O tempo de execução inclui verificações para ajudar a detetar estas violações.
Para saber mais sobre como o Durable Task Framework executa funções do orquestrador, consulte o código-fonte Durable Task no GitHub. Em particular, veja TaskOrchestrationExecutor.cs e TaskOrchestrationContext.cs.
Tarefas que podem esperar em segurança nos orquestradores são por vezes chamadas de tarefas duradouras. O Durable Task Framework cria e gere estas tarefas. Exemplos incluem as tarefas devolvidas por CallActivityAsync, WaitForExternalEvent e CreateTimer em .NET orquestradores.
Uma lista de objetos TaskCompletionSource em .NET gere internamente estas tarefas duradouras. Durante a reprodução, o código do orquestrador cria estas tarefas. O despachante completa-os à medida que enumera os eventos históricos correspondentes.
O runtime executa as tarefas de forma síncrona num único thread até que o histórico seja reproduzido. Se uma tarefa durável não terminar até ao final da reprodução do histórico, o ambiente de execução toma as ações apropriadas. Por exemplo, o runtime pode enfileirar uma mensagem para chamar uma atividade.
Este comportamento em tempo de execução explica porque o seu orquestrador não pode usar await ou yield em uma tarefa não persistente. O thread do dispatcher não pode esperar que a tarefa termine, e os callbacks dessa tarefa podem corromper o estado de rastreamento do orquestrador. O tempo de execução inclui verificações para ajudar a detetar estas violações.
Para saber mais sobre como o Durable Task Framework executa orquestradores, consulte o código-fonte Durable Task em GitHub. Em particular, veja TaskOrchestrationExecutor.cs e TaskOrchestrationContext.cs.