O Azure Durable Functions estende o paradigma da computação sem servidor, introduzindo o conceito de orquestração de funções, permitindo a definição de fluxos de trabalho mais complexos. Se já imaginou usá-los, a Microsoft publicou um guia para ajudar os desenvolvedores a iniciar sua jornada nesta nova funcionalidade.
O Azure Functions é um serviço fornecido pela Microsoft que inicia basicamente a partir de um disparo; ele recebe uma entrada, executa um processamento e produz uma saída como resultado. O Durable Functions amplia este conceito, pois de certa maneira, acrescenta o estado a algo que é no fundo, sem estado. O Durable Functions é um conjunto de funções chamadas de uma função orquestradora que executa as demais funções em uma única transação, permitindo assim a escrita de código de fluxos de trabalho complexos pelos desenvolvedores.
Os dois conceitos fundamentais por de traz da Azure Function são as funções e gatilhos. Uma função é um único método em uma classe estática. Um gatilho é um evento que aciona o código. Uma função é um único método em uma classe estática. Um gatilho é um evento que aciona o código. Gatilhos comuns são Http
, executados a partir de uma requisição HTTP por um usuário, e o Timer
, que é o agendamento da execução de um código. Além disto, o Durable Functions utiliza dois novos conceitos:
[FunctionName("GetAllRepositoriesForOrganization")]
public static async Task<List> GetAllRepositoriesForOrganization([ActivityTrigger] DurableActivityContext context)
{
// retrieves the organization name from the Orchestrator function
var organizationName = context.GetInput();
// invoke the API to retrieve the list of repositories of a specific organization
var repositories = (await github.Repository.GetAllForOrg(organizationName)).Select(x => (x.Id, x.Name)).ToList();
return repositories;
}
- Orchestrator functions, que podem ser vistas como uma co-rotina baseada na nuvem. Em outras palavras, uma função orquestradora é capaz de definir o ponto em execução na função, acionar outras funções e esperar até que as execuções sejam completas e então voltar ao ponto em que havia parado.
- Activity functions, são funções que podem ser executadas dentro de um orquestrador. Respondem somente a um orquestrador que as chama. Os resultados de uma activity function são armazenados em um cache e são executados apenas uma vez por instância de orquestrador. Este é um exemplo de uma activity function que recupera uma lista de repositórios do GitHub:
Observe o uso do atributo ActivityTrigger
para o parâmetro DurableActivityContext
: isso é o que qualifica uma função como Activity Function.
Com isso em mente, podemos definir uma função que orquestra a recuperação de todos os repositórios de uma organização e, em seguida, somar o total de pendências em aberto para cada repositório e armazenar o resultado em algum lugar:
[FunctionName("Orchestrator")]
public static async Task RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
// retrieves the organization name from the Orchestrator_HttpStart function
var organizationName = context.GetInput();
// retrieves the list of repositories for an organization by invoking a separate Activity Function.
var repositories = await context.CallActivityAsync<List>("GetAllRepositoriesForOrganization", organizationName);
// Creates an array of task to store the result of each functions
var tasks = new Task[repositories.Count];
for (int i = 0; i < repositories.Count; i++)
{
// Starting a `GetOpenedIssues` activity WITHOUT `async`
// This will starts Activity Functions in parallel instead of sequentially.
tasks[i] = context.CallActivityAsync("GetOpenedIssues", (repositories[i]));
}
// Wait for all Activity Functions to complete execution
await Task.WhenAll(tasks);
// Retrieve the result of each Activity Function and return them in a list
var openedIssues = tasks.Select(x => x.Result).ToList();
// Send the list to an Activity Function to save them to Blob Storage.
await context.CallActivityAsync("SaveRepositories", openedIssues);
return context.InstanceId;
}
No exemplo acima, observe o uso do atributo OrchestrationTrigger
com um parâmetro DurableOrchestrationContext
que qualifica o RunOrchestrator
como uma função do orquestrador.
O que é interessante no código acima é que cada chamada do context
, por si só, é uma Azure Function. Isso significa que a execução do fluxo se beneficia da escalabilidade e da confiabilidade das Azure Functions.
Você pode encontrar o código acima no GitHub e, claro, não deixe de conferir o tutorial da Microsoft para obter todos os detalhes.