Encadeamento de funções é o padrão de executar uma sequência de funções em uma ordem específica. Frequentemente, a saída de uma função precisa ser aplicada à entrada de outra função. Esse artigo descreve a sequência de encadeamento que você cria ao executar o início rápido do Durable Functions (C#, JavaScript, TypeScript, Python, PowerShell ou Java). Para obter mais informações sobre as funções duráveis, consulte visão geral das funções duráveis.
Pré-requisitos
Observação
A versão 4 do modelo de programação Node.js para o Azure Functions está em disponibilidade geral. O modelo v4 foi projetado para fornecer uma experiência mais flexível e intuitiva para desenvolvedores JavaScript e TypeScript. Para obter mais informações sobre as diferenças entre v3 e v4, consulte o guia de migração.
Nos snippets de código a seguir, JavaScript (PM4) denota o modelo de programação v4, a nova experiência.
As funções
Este artigo explica as seguintes funções no aplicativo de exemplo:
-
E1_HelloSequence: uma função de orquestrador que chamas E1_SayHello várias vezes em uma sequência. Ela armazena as saídas das chamadas E1_SayHello e registra os resultados.
-
E1_SayHello: uma função de atividade que precede uma cadeia de caracteres com "Hello".
-
HttpStart: uma função de cliente durável disparada por HTTP que inicia uma instância do orquestrador.
Função de orquestrador E1_HelloSequence
[FunctionName("E1_HelloSequence")]
public static async Task<List<string>> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello_DirectInput", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
Todas as funções de orquestração em C# devem ter um parâmetro do tipo DurableOrchestrationContext, que existe no assembly Microsoft.Azure.WebJobs.Extensions.DurableTask. Esse objeto de contexto permite chamar outras funções de atividade e passar parâmetros de entrada usando o método CallActivityAsync.
O código chama E1_SayHello três vezes seguidas com valores de parâmetros diferentes. O valor retornado de cada chamada é adicionado à lista outputs, que é retornada ao final da função.
function.json
Se você usa o Visual Studio Code ou o portal do Azure para desenvolvimento, este é o conteúdo do arquivo function.json para a função de orquestrador. A maioria dos arquivos function.json do orquestrador são quase exatamente iguais a esse.
{
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
],
"disabled": false
}
O importante é o tipo de associação de orchestrationTrigger. Todas as funções de orquestrador devem usar esse tipo de gatilho.
Aviso
Para obedecer a regra de "não fazer E/S" das funções de orquestrador, não use nenhuma associação de entrada ou saída ao usar a associação de gatilho orchestrationTrigger. Se outras associações de entrada ou de saída forem necessárias, elas deverão ser usadas no contexto das funções activityTrigger, que são chamadas pelo orquestrador. Para obter mais informações, confira o artigo Restrições de código na função de orquestrador.
index.js
Aqui está uma função de orquestrador:
const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
context.log("Starting chain sample");
const output = [];
output.push(yield context.df.callActivity("E1_SayHello", "Tokyo"));
output.push(yield context.df.callActivity("E1_SayHello", "Seattle"));
output.push(yield context.df.callActivity("E1_SayHello", "London"));
return output;
});
Todas as funções de orquestração de JavaScript devem incluir o módulo durable-functions. É uma biblioteca que permite escrever Durable Functions em JavaScript. Há três diferenças significativas entre uma função de orquestrador e outras funções de JavaScript:
- A função de orquestrador é uma função de gerador.
- A função é encapsulada em uma chamada para o método
durable-functions do módulo orchestrator (aqui df).
- A função deve ser síncrona. Como o método "orchestrator" lida com a chamada final "context.done", a função deve simplesmente "return".
O objeto context contém um objeto de contexto de orquestração durável df que permite chamar outras funções de atividade e passa parâmetros de entrada usando o método callActivity. O código chama E1_SayHello três vezes em sequência com valores de parâmetros diferentes, usando yield para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona serem retornadas. O valor retornado de cada chamada é adicionado à matriz outputs, que é retornada ao final da função.
const df = require("durable-functions");
const helloActivityName = "sayHello";
df.app.orchestration("helloSequence", function* (context) {
context.log("Starting chain sample");
const output = [];
output.push(yield context.df.callActivity(helloActivityName, "Tokyo"));
output.push(yield context.df.callActivity(helloActivityName, "Seattle"));
output.push(yield context.df.callActivity(helloActivityName, "Cairo"));
return output;
});
Todas as funções de orquestração de JavaScript devem incluir o módulo durable-functions. Esse módulo permite que você escreva Durable Functions em JavaScript. Para usar o modelo de programação de nós V4, você precisa instalar a versão prévia v3.x da versão do durable-functions.
Há duas diferenças significativas entre uma função do orquestrador e outras funções em JavaScript:
- A função de orquestrador é uma função de gerador.
- A função deve ser síncrona. A função deve simplesmente "retornar".
O objeto context contém um objeto de contexto de orquestração durável df que permite chamar outras funções de atividade e passa parâmetros de entrada usando o método callActivity. O código chama sayHello três vezes em sequência com valores de parâmetros diferentes, usando yield para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona serem retornadas. O valor retornado de cada chamada é adicionado à matriz outputs, que é retornada ao final da função.
Observação
Durable Functions em Python estão disponíveis apenas para o runtime do Functions 3.0.
function.json
Se você usa o Visual Studio Code ou o portal do Azure para desenvolvimento, este é o conteúdo do arquivo function.json para a função de orquestrador. A maioria dos arquivos function.json do orquestrador são quase exatamente iguais a esse.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
O importante é o tipo de associação de orchestrationTrigger. Todas as funções de orquestrador devem usar esse tipo de gatilho.
Aviso
Para obedecer a regra de "não fazer E/S" das funções de orquestrador, não use nenhuma associação de entrada ou saída ao usar a associação de gatilho orchestrationTrigger. Se outras associações de entrada ou de saída forem necessárias, elas deverão ser usadas no contexto das funções activityTrigger, que são chamadas pelo orquestrador. Para obter mais informações, confira o artigo Restrições de código na função de orquestrador.
__init__.py
Aqui está uma função de orquestrador:
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
result1 = yield context.call_activity('E1_SayHello', "Tokyo")
result2 = yield context.call_activity('E1_SayHello', "Seattle")
result3 = yield context.call_activity('E1_SayHello', "London")
return [result1, result2, result3]
main = df.Orchestrator.create(orchestrator_function)
Todas as funções de orquestração do Python devem incluir o durable-functions pacote. É uma biblioteca que permite escrever Durable Functions em Python. Há duas diferenças significativas entre uma função de orquestrador e outras funções de Python:
- A função de orquestrador é uma função de gerador.
- O arquivo deve registrar a função de orquestrador como um orquestrador, indicando
main = df.Orchestrator.create(<orchestrator function name>) no final do arquivo. Isso ajuda a distingui-lo de outras funções, auxiliares, declaradas no arquivo.
O objeto context permite chamar outras funções de atividade e passar parâmetros de entrada usando o método call_activity. O código chama E1_SayHello três vezes em sequência com valores de parâmetros diferentes, usando yield para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona serem retornadas. O valor retornado de cada chamada é retornado no final da função.
Função de atividade E1_SayHello
[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] IDurableActivityContext context)
{
string name = context.GetInput<string>();
return $"Hello {name}!";
}
As atividades usam o atributo ActivityTrigger. Use o atributo IDurableActivityContext fornecido para executar ações relacionadas à atividade, como acessar o valor de entrada usando GetInput<T>.
A implementação de E1_SayHello é uma operação de formatação de cadeia de caracteres relativamente simples.
Em vez de associar a um IDurableActivityContext, você pode associar diretamente ao tipo que é passado para a função de atividade. Por exemplo:
[FunctionName("E1_SayHello_DirectInput")]
public static string SayHelloDirectInput([ActivityTrigger] string name)
{
return $"Hello {name}!";
}
E1_SayHello/function.json
O arquivo function.json da função de atividade E1_SayHello é semelhante ao de E1_HelloSequence, exceto por usar um tipo de associação activityTrigger em vez de um tipo de associação orchestrationTrigger.
{
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
],
"disabled": false
}
Observação
Todas as funções de atividade chamadas por uma função de orquestração devem usar a associação activityTrigger.
A implementação de E1_SayHello é uma operação de formatação de cadeia de caracteres relativamente simples.
E1_SayHello/index.js
module.exports = function (context) {
context.done(null, `Hello ${context.bindings.name}!`);
};
Ao contrário de uma função de orquestração, uma função de atividade não precisa de nenhuma configuração especial. A entrada passada a ela pela função do orquestrador está localizada no objeto context.bindings sob o nome da ligação activityTrigger; nesse caso, context.bindings.name. O nome da associação pode ser definido como parâmetro da função exportada e acessado diretamente, que é o que o código de exemplo faz.
A implementação de sayHello é uma operação de formatação de cadeia de caracteres relativamente simples.
const df = require("durable-functions");
const helloActivityName = "sayHello";
df.app.activity(helloActivityName, {
handler: function (input) {
return `Hello ${input}`;
},
});
Ao contrário de uma função de orquestração, uma função de atividade não precisa de nenhuma configuração especial. A entrada passada para ela pela função do orquestrador é o primeiro argumento da função. O segundo argumento é o contexto da invocação, que não é usado nesse exemplo.
E1_SayHello/function.json
O arquivo function.json da função de atividade E1_SayHello é semelhante ao de E1_HelloSequence, exceto por usar um tipo de associação activityTrigger em vez de um tipo de associação orchestrationTrigger.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
]
}
Observação
Todas as funções de atividade chamadas por uma função de orquestração devem usar a associação activityTrigger.
A implementação de E1_SayHello é uma operação de formatação de cadeia de caracteres relativamente simples.
E1_SayHello/__init__.py
def main(name: str) -> str:
return f"Hello {name}!"
Ao contrário de uma função de orquestrador, uma função de atividade não precisa de nenhuma configuração especial. A entrada passada para ela pela função de orquestrador é diretamente acessível como o parâmetro para a função.
Função de cliente HttpStart
Você pode iniciar uma instância da função de orquestrador usando uma função de cliente. Você usará a função HttpStart disparada por HTTP para iniciar instâncias de E1_HelloSequence.
public static class HttpStart
{
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[DurableClient] IDurableClient starter,
string functionName,
ILogger log)
{
// Function input comes from the request content.
object eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync(functionName, eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
Para interagir com orquestradores, a função deve incluir uma associação de entrada DurableClient. Use o cliente para iniciar uma orquestração. Ele também pode ajudar você a retornar uma resposta HTTP contendo URLs para verificar o status da nova orquestração.
HttpStart/function.json
{
"bindings": [
{
"authLevel": "anonymous",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"route": "orchestrators/{functionName}",
"methods": ["post"]
},
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"name": "starter",
"type": "orchestrationClient",
"direction": "in"
}
],
"disabled": false
}
Para interagir com orquestradores, a função deve incluir uma associação de entrada durableClient.
HttpStart/index.js
const df = require("durable-functions");
module.exports = async function (context, req) {
const client = df.getClient(context);
const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
context.log(`Started orchestration with ID = '${instanceId}'.`);
return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};
Use df.getClient para obter um objeto DurableOrchestrationClient. Use o cliente para iniciar uma orquestração. Ele também pode ajudar você a retornar uma resposta HTTP contendo URLs para verificar o status da nova orquestração.
const df = require("durable-functions");
const { app } = require("@azure/functions");
app.http("httpStart", {
route: "orchestrators/{orchestratorName}",
extraInputs: [df.input.durableClient()],
handler: async (request, context) => {
const client = df.getClient(context);
const body = await request.json();
const instanceId = await client.startNew(request.params.orchestratorName, { input: body });
context.log(`Started orchestration with ID = '${instanceId}'.`);
return client.createCheckStatusResponse(request, instanceId);
},
});
Para gerenciar e interagir com orquestradores, a função deve incluir uma vinculação de dados de entrada no durableClient. Essa vinculação precisa ser especificada no argumento extraInputs ao registrar a função. Uma entrada no durableClient pode ser obtida chamando o df.input.durableClient().
Use df.getClient para obter um objeto DurableClient. Use o cliente para iniciar uma orquestração. Ele também pode ajudar você a retornar uma resposta HTTP contendo URLs para verificar o status da nova orquestração.
HttpStart/function.json
{
"scriptFile": "__init__.py",
"bindings": [
{
"authLevel": "anonymous",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"route": "orchestrators/{functionName}",
"methods": [
"post",
"get"
]
},
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"name": "starter",
"type": "durableClient",
"direction": "in"
}
]
}
Para interagir com orquestradores, a função deve incluir uma associação de entrada durableClient.
HttpStart/__init__.py
import logging
import azure.functions as func
import azure.durable_functions as df
async def main(req: func.HttpRequest, starter: str) -> func.HttpResponse:
client = df.DurableOrchestrationClient(starter)
instance_id = await client.start_new(req.route_params["functionName"], None, None)
logging.info(f"Started orchestration with ID = '{instance_id}'.")
return client.create_check_status_response(req, instance_id)
Use o construtor DurableOrchestrationClient para obter um Durable Functions cliente. Use o cliente para iniciar uma orquestração. Ele também pode ajudar você a retornar uma resposta HTTP contendo URLs para verificar o status da nova orquestração.
Execute o exemplo
Para executar a orquestração E1_HelloSequence, envie a solicitação HTTP POST para a função HttpStart.
POST http://{host}/orchestrators/E1_HelloSequence
Observação
O snippet de código HTTP anterior pressupõe que exista uma entrada no arquivo host.json que remove o prefixo api/ padrão de todas as URLs de funções de gatilho HTTP. Você pode encontrar a marcação para essa configuração no arquivo host.json nos exemplos.
Por exemplo, se você estiver executando a amostra em um aplicativo da função chamado "myfunctionapp", substitua "{host}" por "myfunctionapp.azurewebsites.net".
O resultado é uma resposta HTTP 202, como esta (resumido para fins de brevidade):
HTTP/1.1 202 Accepted
Content-Length: 719
Content-Type: application/json; charset=utf-8
Location: http://{host}/runtime/webhooks/durabletask/instances/96924899c16d43b08a536de376ac786b?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
(...trimmed...)
Neste ponto, a orquestração é enfileirada e começa a ser executada imediatamente. A URL no cabeçalho Location pode ser usada para verificar o status da execução.
GET http://{host}/runtime/webhooks/durabletask/instances/96924899c16d43b08a536de376ac786b?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
O resultado é o status da orquestração. Ela é executada e concluída rapidamente e você a verá com estado Concluído, com uma resposta semelhante esta (resumida para fins de brevidade):
HTTP/1.1 200 OK
Content-Length: 179
Content-Type: application/json; charset=utf-8
{"runtimeStatus":"Completed","input":null,"output":["Hello Tokyo!","Hello Seattle!","Hello London!"],"createdTime":"2017-06-29T05:24:57Z","lastUpdatedTime":"2017-06-29T05:24:59Z"}
Como você pode ver, o runtimeStatus da instância é Concluído e o output contém o resultado serializado em JSON da execução da função de orquestrador.
Observação
Você pode implementar uma lógica inicial semelhante a outros tipos de gatilho, como queueTrigger, eventHubTrigger ou timerTrigger.
Examine os logs de execução da função. A função E1_HelloSequence foi iniciada e concluída várias vezes devido ao comportamento de reprodução descrito no tópico confiabilidade da orquestração. Por outro lado, houve apenas três execuções de E1_SayHello, uma vez que as execuções dessas funções não são repetidas.
Próximas etapas
Este exemplo demonstrou uma orquestração de encadeamento de função simples. O próximo exemplo mostra como implementar o padrão de fan-out/fan-in.