Durable Functions é uma extensão de Funções do Azure que lhe permite escrever funções com estado num ambiente de computação sem servidor. A extensão permite-lhe definir fluxos de trabalho com monitorização de estado ao escrever funções de orquestrador e entidades com monitorização de estado ao escrever funções de entidade com o modelo de programação Funções do Azure. Nos bastidores, a extensão gere o estado, os pontos de verificação e os reinícios automaticamente, permitindo-lhe concentrar-se na lógica de negócio.
Linguagens suportadas
Durable Functions foi concebido para trabalhar com todas as linguagens de programação Funções do Azure, mas pode ter requisitos mínimos diferentes para cada linguagem. A tabela seguinte mostra as configurações mínimas suportadas da aplicação:
O principal caso de utilização para Durable Functions é simplificar requisitos de coordenação complexos e com monitorização de estado em aplicações sem servidor. As secções seguintes descrevem padrões de aplicação típicos que podem beneficiar de Durable Functions:
No padrão de encadeamento de funções, uma sequência de funções é executada por uma ordem específica. Neste padrão, a saída de uma função é aplicada à entrada de outra função. A utilização de filas entre cada função garante que o sistema permanece durável e dimensionável, apesar de existir um fluxo de controlo de uma função para a outra.
Pode utilizar Durable Functions para implementar o padrão de encadeamento de funções de forma concisa, conforme mostrado no exemplo seguinte.
Neste exemplo, os valores F1, F2, F3e F4 são os nomes de outras funções na mesma aplicação de funções. Pode implementar o fluxo de controlo com construções normais de codificação imperativa. O código é executado de cima para baixo. O código pode envolver semântica de fluxo de controlo de linguagem existente, como condicionais e ciclos. Pode incluir lógica de processamento de erros em try//catchfinally 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 por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama await, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior await . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
[Function("Chaining")]
public static async Task<object> Run(
[OrchestrationTrigger] TaskOrchestrationContext 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 por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama await, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior await . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
Pode utilizar o context.df objeto para invocar outras funções por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama yield, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior yield . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
Nota
O context objeto em JavaScript representa todo o contexto da função. Aceda ao contexto Durable Functions com a df propriedade no contexto principal.
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
x = yield context.call_activity("F1", None)
y = yield context.call_activity("F2", x)
z = yield context.call_activity("F3", y)
result = yield context.call_activity("F4", z)
return result
main = df.Orchestrator.create(orchestrator_function)
Pode utilizar o context objeto para invocar outras funções por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama yield, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior yield . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
Nota
O context objeto em Python representa o contexto de orquestração. Aceda ao contexto de Funções do Azure principal com a function_context propriedade no contexto de orquestração.
Pode utilizar o Invoke-DurableActivity comando para invocar outras funções por nome, parâmetros de passagem e devolver a saída da função. Sempre que o código chama Invoke-DurableActivity sem o NoWait comutador, o Durable Functions framework cria um ponto de verificação do progresso da instância de função atual. Se o processo ou a máquina virtual forem reciclados a meio da execução, a instância de função será retomada a partir da chamada anterior Invoke-DurableActivity . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
@FunctionName("Chaining")
public double functionChaining(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
String input = ctx.getInput(String.class);
int x = ctx.callActivity("F1", input, int.class).await();
int y = ctx.callActivity("F2", x, int.class).await();
int z = ctx.callActivity("F3", y, int.class).await();
return ctx.callActivity("F4", z, double.class).await();
}
Pode utilizar o ctx objeto para invocar outras funções por nome, parâmetros de passagem e devolver a saída da função. O resultado destas chamadas de método é um Task<V> objeto em que V é o tipo de dados devolvidos pela função invocada. Sempre que chamar Task<V>.await(), o Durable Functions framework define o progresso da instância de função atual. Se o processo reciclar inesperadamente a meio da execução, a instância da função será retomada a partir da chamada anterior Task<V>.await() . Para obter mais informações, consulte a secção seguinte, Padrão n.º 2: Fan out/fan in.
Padrão n.º 2: Fan out/fan in
No padrão fan out/fan in, executa várias funções em paralelo e, em seguida, aguarda que todas as funções terminem. Muitas vezes, alguns trabalhos de agregação são realizados nos resultados que são devolvidos das funções.
Com as funções normais, pode destacar-se ao fazer com que a função envie várias mensagens para uma fila. Voltar a entrar é muito mais desafiante. Para ativar a ventoinha, numa função normal, escreve código para controlar quando as funções acionadas pela fila terminam e, em seguida, armazena as saídas da função.
A extensão Durable Functions processa 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 fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. Task.WhenAll é chamado para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.
O ponto de verificação automático que ocorre na await chamada em Task.WhenAll garante que uma possível falha a meio do caminho ou reinício não requer o reinício de uma tarefa já concluída.
[Function("FanOutFanIn")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext 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 fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. Task.WhenAll é chamado para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.
O ponto de verificação automático que ocorre na await chamada em Task.WhenAll garante que uma possível falha a meio do caminho ou reinício não requer o reinício de uma tarefa já concluída.
const df = require("durable-functions");
module.exports = df.orchestrator(function*(context) {
const parallelTasks = [];
// Get a list of N work items to process in parallel.
const workBatch = yield context.df.callActivity("F1");
for (let i = 0; i < workBatch.length; i++) {
parallelTasks.push(context.df.callActivity("F2", workBatch[i]));
}
yield context.df.Task.all(parallelTasks);
// Aggregate all N outputs and send the result to F3.
const sum = parallelTasks.reduce((prev, curr) => prev + curr, 0);
yield context.df.callActivity("F3", sum);
});
O trabalho de fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. context.df.Task.all A API é chamada para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.
O ponto de verificação automático que ocorre na yield chamada em context.df.Task.all garante que uma possível falha a meio do caminho ou reinício não requer o reinício de uma tarefa já concluída.
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
# Get a list of N work items to process in parallel.
work_batch = yield context.call_activity("F1", None)
parallel_tasks = [ context.call_activity("F2", b) for b in work_batch ]
outputs = yield context.task_all(parallel_tasks)
# Aggregate all N outputs and send the result to F3.
total = sum(outputs)
yield context.call_activity("F3", total)
main = df.Orchestrator.create(orchestrator_function)
O trabalho de fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. context.task_all A API é chamada para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.
O ponto de verificação automático que ocorre na yield chamada em context.task_all garante que uma possível falha a meio do caminho ou reinício não requer o reinício de uma tarefa já concluída.
param($Context)
# Get a list of work items to process in parallel.
$WorkBatch = Invoke-DurableActivity -FunctionName 'F1'
$ParallelTasks =
foreach ($WorkItem in $WorkBatch) {
Invoke-DurableActivity -FunctionName 'F2' -Input $WorkItem -NoWait
}
$Outputs = Wait-ActivityFunction -Task $ParallelTasks
# Aggregate all outputs and send the result to F3.
$Total = ($Outputs | Measure-Object -Sum).Sum
Invoke-DurableActivity -FunctionName 'F3' -Input $Total
O trabalho de fan-out é distribuído para várias instâncias da F2 função. Tenha em atenção a utilização do NoWait comutador na invocação da F2 função: este comutador permite que o orquestrador continue a invocar F2 sem aguardar a conclusão da atividade. O trabalho é monitorizado através de uma lista dinâmica de tarefas. O Wait-ActivityFunction comando é chamado para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e transmitidas para a F3 função.
O ponto de verificação automático que ocorre na Wait-ActivityFunction chamada garante que uma possível falha a meio ou reinício não requer o reinício de uma tarefa já concluída.
@FunctionName("FanOutFanIn")
public Integer fanOutFanInOrchestrator(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
// Get the list of work-items to process in parallel
List<?> batch = ctx.callActivity("F1", List.class).await();
// Schedule each task to run in parallel
List<Task<Integer>> parallelTasks = batch.stream()
.map(item -> ctx.callActivity("F2", item, Integer.class))
.collect(Collectors.toList());
// Wait for all tasks to complete, then return the aggregated sum of the results
List<Integer> results = ctx.allOf(parallelTasks).await();
return results.stream().reduce(0, Integer::sum);
}
O trabalho de fan-out é distribuído para várias instâncias da F2 função. O trabalho é monitorizado através de uma lista dinâmica de tarefas. ctx.allOf(parallelTasks).await() é chamado para aguardar que todas as funções chamadas sejam concluídas. Em seguida, as saídas da F2 função são agregadas a partir da lista de tarefas dinâmicas e devolvidas como saída da função do orquestrador.
O ponto de verificação automático que ocorre na .await() chamada em ctx.allOf(parallelTasks) garante que uma reciclagem de processos inesperada não requer o reinício de tarefas já concluídas.
Nota
Em circunstâncias raras, é possível que uma falha possa ocorrer na janela após a conclusão de uma função de atividade, mas antes da conclusão ser guardada no histórico de orquestração. Se isto acontecer, a função de atividade será novamente executada desde o início após a recuperação do processo.
Padrão n.º 3: APIs HTTP assíncronas
O padrão da API HTTP assíncrona resolve o problema de coordenar o estado das operações de execução prolongada com clientes externos. Uma forma comum de implementar este padrão é ter um ponto final HTTP acionar a ação de execução prolongada. Em seguida, redirecione o cliente para um ponto final de estado que o cliente consulta para saber quando a operação está concluída.
Durable Functions fornece suporte incorporado para este padrão, simplificando ou mesmo removendo o código que precisa de escrever para interagir com execuções de funções de execução prolongada. Por exemplo, os Durable Functions exemplos de início rápido (C#, JavaScript, Python, PowerShell e Java) mostram um comando REST simples que pode utilizar para iniciar novas instâncias de função do orquestrador. Após o início de uma instância, a extensão expõe as APIs HTTP do webhook que consultam o estado da função do orquestrador.
O exemplo seguinte mostra comandos REST que iniciam um orquestrador e consultam o respetivo estado. Para maior clareza, alguns detalhes do protocolo são omitidos do exemplo.
Uma vez que o Durable Functions runtime gere o seu estado, não precisa de implementar o seu próprio mecanismo de controlo de estado.
A extensão Durable Functions expõe AS APIs HTTP incorporadas que gerem orquestrações de longa duração. Em alternativa, pode implementar este padrão utilizando os seus próprios acionadores de função (como HTTP, uma fila ou Hubs de Eventos do Azure) e o enlace do cliente de orquestração. Por exemplo, pode utilizar uma mensagem de fila para acionar a terminação. Em alternativa, pode utilizar um acionador HTTP protegido por uma política de autenticação do Azure Active Directory em vez das APIs HTTP incorporadas que utilizam uma chave gerada para autenticação.
Para obter mais informações, consulte o artigo funcionalidades HTTP, que explica como pode expor processos assíncronos e de execução prolongada através de HTTP com a extensão Durable Functions.
Padrão n.º 4: Monitorizar
O padrão de monitorização refere-se a um processo flexível e periódico num fluxo de trabalho. Um exemplo é consultar até que sejam cumpridas condições específicas. Pode utilizar um acionador de temporizador regular para abordar um cenário básico, como uma tarefa de limpeza periódica, mas o intervalo é estático e a gestão de durações de instâncias torna-se complexa. Pode utilizar Durable Functions para criar intervalos de periodicidade flexíveis, gerir durações de tarefas e criar vários processos de monitorização a partir de uma única orquestração.
Um exemplo do padrão de monitorização é inverter o cenário da API HTTP assíncrona anterior. Em vez de expor um ponto final para um cliente externo monitorizar uma operação de execução prolongada, o monitor de execução prolongada consome um ponto final externo e, em seguida, aguarda uma alteração de estado.
Em algumas linhas de código, pode utilizar Durable Functions para criar vários monitores que observam pontos finais arbitrários. Os monitores podem terminar a execução quando uma condição é cumprida ou outra função pode utilizar o cliente de orquestração durável para terminar os monitores. Pode alterar o intervalo de wait um monitor com base numa condição específica (por exemplo, backoff exponencial.)
[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.
}
[Function("MonitorJobStatus")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context, int jobId)
{
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.
}
const df = require("durable-functions");
const moment = require("moment");
module.exports = df.orchestrator(function*(context) {
const jobId = context.df.getInput();
const pollingInterval = getPollingInterval();
const expiryTime = getExpiryTime();
while (moment.utc(context.df.currentUtcDateTime).isBefore(expiryTime)) {
const jobStatus = yield context.df.callActivity("GetJobStatus", jobId);
if (jobStatus === "Completed") {
// Perform an action when a condition is met.
yield context.df.callActivity("SendAlert", machineId);
break;
}
// Orchestration sleeps until this time.
const nextCheck = moment.utc(context.df.currentUtcDateTime).add(pollingInterval, 's');
yield context.df.createTimer(nextCheck.toDate());
}
// Perform more work here, or let the orchestration end.
});
import azure.durable_functions as df
import json
from datetime import timedelta
def orchestrator_function(context: df.DurableOrchestrationContext):
job = json.loads(context.get_input())
job_id = job["jobId"]
polling_interval = job["pollingInterval"]
expiry_time = job["expiryTime"]
while context.current_utc_datetime < expiry_time:
job_status = yield context.call_activity("GetJobStatus", job_id)
if job_status == "Completed":
# Perform an action when a condition is met.
yield context.call_activity("SendAlert", job_id)
break
# Orchestration sleeps until this time.
next_check = context.current_utc_datetime + timedelta(seconds=polling_interval)
yield context.create_timer(next_check)
# Perform more work here, or let the orchestration end.
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$output = @()
$jobId = $Context.Input.JobId
$machineId = $Context.Input.MachineId
$pollingInterval = New-TimeSpan -Seconds $Context.Input.PollingInterval
$expiryTime = $Context.Input.ExpiryTime
while ($Context.CurrentUtcDateTime -lt $expiryTime) {
$jobStatus = Invoke-DurableActivity -FunctionName 'GetJobStatus' -Input $jobId
if ($jobStatus -eq "Completed") {
# Perform an action when a condition is met.
$output += Invoke-DurableActivity -FunctionName 'SendAlert' -Input $machineId
break
}
# Orchestration sleeps until this time.
Start-DurableTimer -Duration $pollingInterval
}
# Perform more work here, or let the orchestration end.
$output
@FunctionName("Monitor")
public String monitorOrchestrator(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
JobInfo jobInfo = ctx.getInput(JobInfo.class);
String jobId = jobInfo.getJobId();
Instant expiryTime = jobInfo.getExpirationTime();
while (ctx.getCurrentInstant().compareTo(expiryTime) < 0) {
String status = ctx.callActivity("GetJobStatus", jobId, String.class).await();
// Perform an action when a condition is met
if (status.equals("Completed")) {
// send an alert and exit
ctx.callActivity("SendAlert", jobId).await();
break;
}
// wait N minutes before doing the next poll
Duration pollingDelay = jobInfo.getPollingDelay();
ctx.createTimer(pollingDelay).await();
}
return "done";
}
Quando um pedido é recebido, é criada uma nova instância de orquestração para esse ID de tarefa. A instância consulta um estado até que uma condição seja cumprida ou até que um tempo limite expire. Um temporizador durável controla o intervalo de consulta. Em seguida, pode executar mais trabalho ou a orquestração pode terminar.
Padrão n.º 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 são tão disponíveis e tão reativas como os serviços cloud. Um processo automatizado pode permitir esta interação através de tempos limite 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 montante em dólares. Se o gestor não aprovar o relatório de despesas no prazo de 72 horas (talvez o gestor tenha ido de férias), um processo de escalamento inicia-se para obter a aprovação de outra pessoa (talvez do gestor do gestor).
Pode implementar o padrão neste exemplo com uma função de orquestrador. O orquestrador utiliza um temporizador durável para pedir aprovação. O orquestrador aumenta se ocorrer um tempo limite. O orquestrador aguarda por um evento externo, como uma notificação gerada por uma interação humana.
Estes exemplos criam um processo de aprovação para demonstrar o padrão de interação humana:
Para criar o temporizador durável, chame context.CreateTimer. A notificação é recebida por context.WaitForExternalEvent. Em seguida, Task.WhenAny é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).
Para criar o temporizador durável, chame context.CreateTimer. A notificação é recebida por context.WaitForExternalEvent. Em seguida, Task.WhenAny é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).
Para criar o temporizador durável, chame context.df.createTimer. A notificação é recebida por context.df.waitForExternalEvent. Em seguida, context.df.Task.any é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).
Para criar o temporizador durável, chame context.create_timer. A notificação é recebida por context.wait_for_external_event. Em seguida, context.task_any é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).
Para criar o temporizador durável, chame Start-DurableTimer. A notificação é recebida por Start-DurableExternalEventListener. Em seguida, Wait-DurableTask é chamado para decidir se deve escalar (o tempo limite ocorre primeiro) ou processar a aprovação (a aprovação é recebida antes do tempo limite).
@FunctionName("ApprovalWorkflow")
public void approvalWorkflow(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
ApprovalInfo approvalInfo = ctx.getInput(ApprovalInfo.class);
ctx.callActivity("RequestApproval", approvalInfo).await();
Duration timeout = Duration.ofHours(72);
try {
// Wait for an approval. A TaskCanceledException will be thrown if the timeout expires.
boolean approved = ctx.waitForExternalEvent("ApprovalEvent", timeout, boolean.class).await();
approvalInfo.setApproved(approved);
ctx.callActivity("ProcessApproval", approvalInfo).await();
} catch (TaskCanceledException timeoutEx) {
ctx.callActivity("Escalate", approvalInfo).await();
}
}
A ctx.waitForExternalEvent(...).await() chamada de método coloca a orquestração em pausa até receber um evento com o nome ApprovalEvent, que tem um boolean payload. Se o evento for recebido, é chamada uma função de atividade para processar o resultado da aprovação. No entanto, se nenhum evento deste tipo for recebido antes de ( timeout 72 horas) expirar, um TaskCanceledException é gerado e a Escalate função de atividade é chamada.
Nota
Não é cobrado o tempo despendido à espera de eventos externos durante a execução no Plano de consumo.
Um cliente externo pode entregar a notificação de evento a uma função de orquestrador em espera com as APIs HTTP incorporadas:
O sexto padrão consiste em agregar dados de eventos ao longo de um período de tempo numa única entidade endereçável. Neste padrão, os dados que estão a ser agregados podem ser provenientes de várias origens, podem ser entregues em lotes ou podem estar dispersos por longos períodos de tempo. O agregador poderá ter de tomar medidas sobre os dados de eventos à medida que chegam e os clientes externos poderão ter de consultar os dados agregados.
O mais complicado de tentar implementar este padrão com funções normais e sem estado é que o controlo de simultaneidade se torna um grande desafio. Não só tem de se preocupar com a modificação de vários threads ao mesmo tempo, como também tem de se preocupar em garantir que o agregador só é executado numa única VM de cada vez.
Pode utilizar entidades Durable 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;
}
}
As entidades duráveis também podem ser modeladas como classes no .NET. Este modelo pode ser útil se a lista de operações for fixa e se tornar grande. O exemplo seguinte é uma implementação equivalente da Counter entidade com 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>();
}
Atualmente, as entidades duráveis não são suportadas no . Trabalho isolado da NET.
Atualmente, as entidades duráveis não são suportadas no PowerShell.
Atualmente, as entidades duráveis não são suportadas em Java.
Os clientes podem colocar em fila de espera operações para (também conhecidas como "sinalização") uma função de entidade com o enlace 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 no .NET para sinalizar entidades de forma segura. Além da sinalização, os clientes também podem consultar o estado de uma função de entidade através de métodos seguros de tipo no enlace do cliente de orquestração.
Atualmente, as entidades duráveis não são suportadas no . Trabalho isolado da NET.
Atualmente, as entidades duráveis não são suportadas no PowerShell.
Atualmente, as entidades duráveis não são suportadas em Java.
As funções de entidade estão disponíveis no Durable Functions 2.0 e superior para C#, JavaScript e Python.
A tecnologia
Nos bastidores, a extensão Durable Functions é criada sobre a Durable Task Framework, uma biblioteca open source no GitHub que é utilizada para criar fluxos de trabalho em código. Tal como Funções do Azure é a evolução sem servidor dos WebJobs do Azure, Durable Functions é a evolução sem servidor do Durable Task Framework. A Microsoft e outras organizações utilizam extensivamente o Durable Task Framework para automatizar processos críticos para a missão. É um ajuste natural para o ambiente de Funções do Azure sem servidor.
Durable Functions são faturados da mesma forma que Funções do Azure. Para obter mais informações, veja Funções do Azure preços. Ao executar funções de orquestrador no plano de Consumo de Funções do Azure, existem alguns comportamentos de faturação a ter em conta. Para obter mais informações sobre estes comportamentos, consulte o artigo Durable Functions faturação.
Saltar para a direita
Pode começar a utilizar Durable Functions em menos de 10 minutos ao concluir um destes tutoriais de início rápido específicos do idioma:
Nestes inícios rápidos, vai criar e testar localmente uma função durável "hello world". Em seguida, publique o código de função no Azure. A função que criar orquestra e agrupar chama para outras funções.
Publicações
Durable Functions é desenvolvido em colaboração com a Microsoft Research. Como resultado, a equipa Durable Functions produz ativamente trabalhos de investigação e artefactos; estes incluem:
O vídeo seguinte destaca os benefícios do Durable Functions:
Para uma discussão mais aprofundada sobre Durable Functions e a tecnologia subjacente, veja o seguinte vídeo (está focado no .NET, mas os conceitos também se aplicam a outros idiomas suportados):