Encadeamento de funções refere-se ao padrão de execução de uma sequência de funções em uma ordem específica. Muitas vezes, a saída de uma função precisa ser aplicada à entrada de outra função. Este artigo descreve a sequência de encadeamento que você cria ao concluir o início rápido do Durable Functions (C#, JavaScript, TypeScript, Python, PowerShell ou Java). Para obter mais informações sobre funções duráveis, consulte Visão geral de funções duráveis.
Pré-requisitos
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.
As funções
Este artigo explica as seguintes funções no aplicativo de exemplo:
E1_HelloSequence
: Uma função de orquestrador que chama E1_SayHello
várias vezes em uma sequência. Ele armazena as saídas das E1_SayHello
chamadas e registra os resultados.
E1_SayHello
: Uma função de atividade que precede uma cadeia de caracteres com "Olá".
HttpStart
: Uma função de cliente durável acionada por HTTP que inicia uma instância do orquestrador.
E1_HelloSequence função orquestradora
[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 C# devem ter um parâmetro do tipo DurableOrchestrationContext
, que existe no Microsoft.Azure.WebJobs.Extensions.DurableTask
assembly. Este objeto de contexto permite chamar outras funções de atividade e passar parâmetros de entrada usando seu CallActivityAsync
método.
O código chama E1_SayHello
três vezes em sequência com diferentes valores de parâmetro. O valor de retorno de cada chamada é adicionado à outputs
lista, que é retornada no final da função.
function.json
Se você usar o Visual Studio Code ou o portal do Azure para desenvolvimento, aqui está o conteúdo do arquivo de function.json para a função orchestrator. A maioria dos arquivos function.json orquestrador se parece quase exatamente com isso.
{
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
],
"disabled": false
}
O importante é o orchestrationTrigger
tipo de ligação. Todas as funções do orquestrador devem usar esse tipo de gatilho.
Aviso
Para cumprir a regra "sem E/S" das funções do orquestrador, não use nenhuma ligação de entrada ou saída ao usar a orchestrationTrigger
ligação de gatilho. Se outras ligações de entrada ou saída forem necessárias, elas devem ser usadas no contexto de activityTrigger
funções, que são chamadas pelo orquestrador. Para obter mais informações, consulte o artigo sobre restrições de código de função do orchestrator.
index.js
Aqui está a 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 JavaScript devem incluir o durable-functions
módulo. É uma biblioteca que permite escrever funções duráveis em JavaScript. Há três diferenças significativas entre uma função orchestrator e outras funções JavaScript:
- A função orquestradora é uma função geradora.
- A função é envolvida em uma chamada para o
durable-functions
método do orchestrator
módulo (aqui df
).
- A função deve ser síncrona. Como o método 'orchestrator' lida com a chamada final para 'context.done', a função deve simplesmente 'retornar'.
O context
objeto contém um df
objeto de contexto de orquestração durável que permite chamar outras funções de atividade e passar parâmetros de entrada usando seu callActivity
método. O código chama E1_SayHello
três vezes em sequência com diferentes valores de parâmetro, usando yield
para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona para ser retornado. O valor de retorno de cada chamada é adicionado à outputs
matriz, que é retornada no 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 JavaScript devem incluir o durable-functions
módulo. Este módulo permite que você escreva funções duráveis em JavaScript. Para usar o modelo de programação de nó V4, você precisa instalar a versão de visualização v3.x
do durable-functions
.
Há duas diferenças significativas entre uma função orchestrator e outras funções JavaScript:
- A função orquestradora é uma função geradora.
- A função deve ser síncrona. A função deve simplesmente "retornar".
O context
objeto contém um df
objeto de contexto de orquestração durável que permite chamar outras funções de atividade e passar parâmetros de entrada usando seu callActivity
método. O código chama sayHello
três vezes em sequência com diferentes valores de parâmetro, usando yield
para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona para ser retornado. O valor de retorno de cada chamada é adicionado à outputs
matriz, que é retornada no final da função.
Nota
Python Durable Functions estão disponíveis apenas para o tempo de execução do Functions 3.0.
function.json
Se você usar o Visual Studio Code ou o portal do Azure para desenvolvimento, aqui está o conteúdo do arquivo de function.json para a função orchestrator. A maioria dos arquivos function.json orquestrador se parece quase exatamente com isso.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
O importante é o orchestrationTrigger
tipo de ligação. Todas as funções do orquestrador devem usar esse tipo de gatilho.
Aviso
Para cumprir a regra "sem E/S" das funções do orquestrador, não use nenhuma ligação de entrada ou saída ao usar a orchestrationTrigger
ligação de gatilho. Se outras ligações de entrada ou saída forem necessárias, elas devem ser usadas no contexto de activityTrigger
funções, que são chamadas pelo orquestrador. Para obter mais informações, consulte o artigo sobre restrições de código de função do orchestrator.
__init__.py
Aqui está a 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 Python devem incluir o durable-functions
pacote. É uma biblioteca que permite escrever funções duráveis em Python. Existem duas diferenças significativas entre uma função orchestrator e outras funções Python:
- A função orquestradora é uma função geradora.
- O arquivo deve registrar a função do 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 context
objeto permite chamar outras funções de atividade e passar parâmetros de entrada usando seu call_activity
método. O código chama E1_SayHello
três vezes em sequência com diferentes valores de parâmetro, usando yield
para indicar que a execução deve aguardar as chamadas de função de atividade assíncrona para ser retornado. O valor de retorno de cada chamada é retornado no final da função.
E1_SayHello função de atividade
[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] IDurableActivityContext context)
{
string name = context.GetInput<string>();
return $"Hello {name}!";
}
As atividades usam o ActivityTrigger
atributo. Use o fornecido IDurableActivityContext
para executar ações relacionadas à atividade, como acessar o valor de entrada usando GetInput<T>
.
A implementação de é uma operação de formatação de cadeia de E1_SayHello
caracteres relativamente trivial.
Em vez de vincular a um IDurableActivityContext
, você pode vincular 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 de function.json para a função E1_SayHello
de atividade é semelhante ao de exceto que ele usa um activityTrigger
tipo de ligação em vez de um orchestrationTrigger
tipo de E1_HelloSequence
ligação.
{
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
],
"disabled": false
}
Nota
Todas as funções de atividade chamadas por uma função de orquestração devem usar a activityTrigger
ligação.
A implementação de é uma operação de formatação de cadeia de E1_SayHello
caracteres relativamente trivial.
E1_SayHello/index.js
module.exports = function (context) {
context.done(null, `Hello ${context.bindings.name}!`);
};
Ao contrário da função de orquestração, uma função de atividade não precisa de configuração especial. A entrada passada para ele pela função orchestrator está localizada no context.bindings
objeto sob o nome da activityTrigger
ligação - neste caso, context.bindings.name
. O nome da associação pode ser definido como um parâmetro da função exportada e acessado diretamente, que é o que o código de exemplo faz.
A implementação de é uma operação de formatação de cadeia de sayHello
caracteres relativamente trivial.
const df = require("durable-functions");
const helloActivityName = "sayHello";
df.app.activity(helloActivityName, {
handler: function (input) {
return `Hello ${input}`;
},
});
Ao contrário da função de orquestração, uma função de atividade não precisa de configuração especial. A entrada passada para ele pela função orquestrador é o primeiro argumento para a função. O segundo argumento é o contexto de invocação, que não é usado neste exemplo.
E1_SayHello/function.json
O arquivo de function.json para a função E1_SayHello
de atividade é semelhante ao de exceto que ele usa um activityTrigger
tipo de ligação em vez de um orchestrationTrigger
tipo de E1_HelloSequence
ligação.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
]
}
Nota
Todas as funções de atividade chamadas por uma função de orquestração devem usar a activityTrigger
ligação.
A implementação de é uma operação de formatação de cadeia de E1_SayHello
caracteres relativamente trivial.
E1_SayHello/__init__.py
def main(name: str) -> str:
return f"Hello {name}!"
Ao contrário da função orquestradora, uma função de atividade não precisa de configuração especial. A entrada passada para ele pela função orchestrator é 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 orchestrator usando uma função de cliente. Você usará a função acionada HttpStart
HTTP para iniciar instâncias do 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 DurableClient
ligação de entrada. Você usa o cliente para iniciar uma orquestração. Ele também pode ajudá-lo 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 durableClient
ligação de entrada.
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 DurableOrchestrationClient
objeto. Você usa o cliente para iniciar uma orquestração. Ele também pode ajudá-lo 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 precisa de uma durableClient
ligação de entrada. Essa ligação precisa ser especificada no extraInputs
argumento ao registrar a função. Uma durableClient
entrada pode ser obtida ligando para df.input.durableClient()
.
Use df.getClient
para obter um DurableClient
objeto. Você usa o cliente para iniciar uma orquestração. Ele também pode ajudá-lo 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 durableClient
ligação de entrada.
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 DurableOrchestrationClient
construtor para obter um cliente Durable Functions. Você usa o cliente para iniciar uma orquestração. Ele também pode ajudá-lo a retornar uma resposta HTTP contendo URLs para verificar o status da nova orquestração.
Executar o exemplo
Para executar a E1_HelloSequence
orquestração, envie a seguinte solicitação HTTP POST para a HttpStart
função.
POST http://{host}/orchestrators/E1_HelloSequence
Nota
O trecho HTTP anterior pressupõe que há uma entrada no arquivo que remove o prefixo padrão api/
de todas as URLs de funções de gatilho host.json
HTTP. Você pode encontrar a marcação para essa configuração no host.json
arquivo nos exemplos.
Por exemplo, se você estiver executando o exemplo em um aplicativo de função chamado "myfunctionapp", substitua "{host}" por "myfunctionapp.azurewebsites.net".
O resultado é uma resposta HTTP 202, como esta (cortada para 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. O URL no Location
cabeçalho pode ser usado 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. Ele é executado e concluído rapidamente, para que você o veja no estado Concluído com uma resposta semelhante a esta (cortada para 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 é Completed e contém output
o resultado serializado em JSON da execução da função orchestrator.
Nota
Você pode implementar lógica inicial semelhante para outros tipos de gatilho, como queueTrigger
, eventHubTrigger
ou timerTrigger
.
Observe os logs de execução da função. A E1_HelloSequence
função foi iniciada e concluída várias vezes devido ao comportamento de repetição descrito no tópico de confiabilidade da orquestração. Por outro lado, houve apenas três execuções de uma vez que essas execuções de E1_SayHello
função não são repetidas.
Próximos passos
Este exemplo demonstrou uma orquestração simples de encadeamento de funções. O próximo exemplo mostra como implementar o padrão fan-out/fan-in.