El encadenamiento de funciones hace referencia al patrón de ejecución de una secuencia de funciones en un orden concreto. A menudo la salida de una función tiene que aplicarse a la entrada de otra función. En este artículo se describe la secuencia de encadenamiento que se crea al completar el inicio rápido de Durable Functions (C#, JavaScript, TypeScript, Python, PowerShell o Java). Para más información sobre Durable Functions, consulte Durable Functions overview (Información general de Durable Functions).
Prerequisites
Nota:
La versión 4 del modelo de programación de Node.js para Azure Functions está disponible de forma general. El nuevo modelo v4 está diseñado para que los desarrolladores de JavaScript y TypeScript tengan una experiencia más flexible e intuitiva. Obtenga más información sobre las diferencias entre v3 y v4 en la guía de migración.
En los siguientes fragmentos de código, JavaScript (PM4) denota el modelo de programación V4, la nueva experiencia.
Funciones
En este artículo se explican las funciones siguientes en la aplicación de ejemplo:
E1_HelloSequence
: una función de orquestador que llama a E1_SayHello
varias veces en una secuencia. Almacena las salidas de las llamadas de E1_SayHello
y registra los resultados.
E1_SayHello
: una función de actividad que antepone una cadena con "Hello".
HttpStart
: función de cliente de larga duración desencadenada por HTTP que inicia una instancia del orquestador.
Función de orquestador 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 las funciones de orquestación de C# deben tener un parámetro de tipo DurableOrchestrationContext
, que existe en el ensamblado Microsoft.Azure.WebJobs.Extensions.DurableTask
. Este objeto de contexto le permite llamar a otras funciones de actividad y pasar parámetros de entrada con su método CallActivityAsync
.
El código llama a E1_SayHello
tres veces en secuencia con distintos valores de parámetro. El valor devuelto de cada llamada se agrega a la lista outputs
, que se devuelve al final de la función.
function.json
Si utiliza Visual Studio Code o Azure Portal para el desarrollo, aquí tiene el contenido del archivo function.json para la función de orquestador. La mayoría de los archivos function.json de orquestador presentan un aspecto prácticamente idéntico a este.
{
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
],
"disabled": false
}
Lo importante es el tipo de enlace orchestrationTrigger
. Todas las funciones de orquestador deben usar este tipo de desencadenador.
Advertencia
Para cumplir la regla de "ninguna E/S" de las funciones de orquestador, no use ningún enlace de entrada o salida al utilizar el enlace de desencadenador orchestrationTrigger
. Si se necesitan otros enlaces de entrada o salida, deberían utilizarse en el contexto de funciones activityTrigger
en su lugar, a las que llama el orquestador. Para más información, consulte el artículo sobre las restricciones de código de las funciones de orquestador.
index.js
La función de orquestador se muestra a continuación:
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 las funciones de orquestación de JavaScript tienen que incluir el módulo durable-functions
. Se trata de una biblioteca que le permite escribir Durable Functions en JavaScript. Hay tres diferencias importantes entre una función de orquestador y otras funciones de JavaScript:
- La función de orquestador es una función de generador.
- La función se ajusta en una llamada al método
orchestrator
del módulo durable-functions
(aquí df
).
- La función debe ser sincrónica. Dado que el método "orchestrator" se encarga de la llamada final a "context.done", la función debería simplemente devolver "return".
El objeto context
contiene un objeto de contexto de orquestación df
duradero que le permite llamar a otras funciones de actividad y pasar parámetros de entrada mediante su método callActivity
. El código llama a E1_SayHello
tres veces en secuencia con valores de parámetro diferentes, utilizando yield
para indicar que la ejecución debe esperar en las llamadas de función de actividad asincrónica que se van a devolver. El valor devuelto de cada llamada se agrega a la matriz outputs
, que se devuelve al final de la función.
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 las funciones de orquestación de JavaScript tienen que incluir el módulo durable-functions
. Este módulo le permite escribir Durable Functions en JavaScript. Para usar el modelo de programación de nodos V4, debe instalar la versión preliminar v3.x
de durable-functions
.
Hay dos diferencias importantes entre una función de orquestador y otras funciones de JavaScript:
- La función de orquestador es una función de generador.
- La función debe ser sincrónica. La función simplemente debe "devolver".
El objeto context
contiene un objeto de contexto de orquestación df
duradero que le permite llamar a otras funciones de actividad y pasar parámetros de entrada mediante su método callActivity
. El código llama a sayHello
tres veces en secuencia con valores de parámetro diferentes, utilizando yield
para indicar que la ejecución debe esperar en las llamadas de función de actividad asincrónica que se van a devolver. El valor devuelto de cada llamada se agrega a la matriz outputs
, que se devuelve al final de la función.
Nota
Durable Functions de Python solo está disponible para el entorno de ejecución de Functions 3.0.
function.json
Si utiliza Visual Studio Code o Azure Portal para el desarrollo, aquí tiene el contenido del archivo function.json para la función de orquestador. La mayoría de los archivos function.json de orquestador presentan un aspecto prácticamente idéntico a este.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
Lo importante es el tipo de enlace orchestrationTrigger
. Todas las funciones de orquestador deben usar este tipo de desencadenador.
Advertencia
Para cumplir la regla de "ninguna E/S" de las funciones de orquestador, no use ningún enlace de entrada o salida al utilizar el enlace de desencadenador orchestrationTrigger
. Si se necesitan otros enlaces de entrada o salida, deberían utilizarse en el contexto de funciones activityTrigger
en su lugar, a las que llama el orquestador. Para más información, consulte el artículo sobre las restricciones de código de las funciones de orquestador.
__init__.py
La función de orquestador se muestra a continuación:
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 las funciones de orquestación de Python tienen que incluir el paquete durable-functions
. Se trata de una biblioteca que le permite escribir Durable Functions en Python. Hay dos diferencias importantes entre una función de orquestador y otras funciones de Python:
- La función de orquestador es una función de generador.
- El archivo debe indicar
main = df.Orchestrator.create(<orchestrator function name>)
al final del archivo para registrar la función de orquestador como un orquestador. Esto ayuda a distinguirlo de otras funciones auxiliares que se declaran en el archivo.
El objeto context
le permite llamar a otras funciones de actividad y pasar parámetros de entrada con su método call_activity
. El código llama a E1_SayHello
tres veces en secuencia con valores de parámetro diferentes, utilizando yield
para indicar que la ejecución debe esperar en las llamadas de función de actividad asincrónica que se van a devolver. El valor devuelto de cada llamada se devuelve al final de la función.
Función de actividad E1_SayHello
[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] IDurableActivityContext context)
{
string name = context.GetInput<string>();
return $"Hello {name}!";
}
Las actividades usan el atributo ActivityTrigger
. Use el IDurableActivityContext
proporcionado para realizar acciones relacionadas con la actividad, como el acceso al valor de entrada mediante GetInput<T>
.
La implementación de E1_SayHello
es una operación de formato de cadena relativamente sencilla.
En lugar de enlazar a un IDurableActivityContext
, puede enlazar directamente con el tipo que se pasa a la función de actividad. Por ejemplo:
[FunctionName("E1_SayHello_DirectInput")]
public static string SayHelloDirectInput([ActivityTrigger] string name)
{
return $"Hello {name}!";
}
E1_SayHello/function.json
El archivo function.json para la función de actividad E1_SayHello
es similar al de E1_HelloSequence
salvo en que usa un tipo de enlace activityTrigger
en lugar de un tipo de enlace orchestrationTrigger
.
{
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
],
"disabled": false
}
Nota
Todas las funciones de actividad a las que ha llamado una función de orquestación deben utilizar el enlace activityTrigger
.
La implementación de E1_SayHello
es una operación de formato de cadena relativamente sencilla.
E1_SayHello/index.js
module.exports = function (context) {
context.done(null, `Hello ${context.bindings.name}!`);
};
A diferencia de la función de orquestación, una función de actividad no necesita una configuración especial. La entrada que le pasa la función del orquestador se encuentra en el objeto context.bindings
bajo el nombre del enlace activityTrigger
, en este caso context.bindings.name
. El nombre de enlace puede establecerse como un parámetro de la función exportada y se puede acceder a él directamente, que es lo que hace el código de ejemplo.
La implementación de sayHello
es una operación de formato de cadena relativamente sencilla.
const df = require("durable-functions");
const helloActivityName = "sayHello";
df.app.activity(helloActivityName, {
handler: function (input) {
return `Hello ${input}`;
},
});
A diferencia de la función de orquestación, una función de actividad no necesita una configuración especial. La entrada que se le ha pasado a través de la función de orquestador es el primer argumento para la función. El segundo argumento es el contexto de invocación, que no se usa en este ejemplo.
E1_SayHello/function.json
El archivo function.json para la función de actividad E1_SayHello
es similar al de E1_HelloSequence
salvo en que usa un tipo de enlace activityTrigger
en lugar de un tipo de enlace orchestrationTrigger
.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
]
}
Nota
Todas las funciones de actividad a las que ha llamado una función de orquestación deben utilizar el enlace activityTrigger
.
La implementación de E1_SayHello
es una operación de formato de cadena relativamente sencilla.
E1_SayHello/__init__.py
def main(name: str) -> str:
return f"Hello {name}!"
A diferencia de una función de orquestador, una función de actividad no necesita una configuración especial. La entrada que se le ha pasado a través de la función de orquestador es directamente accesible como parámetro para la función.
Función de cliente HttpStart
Puede iniciar una instancia de la función de orquestador mediante una función de cliente. Usará la función desencadenada por HTTP HttpStart
para iniciar instancias 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 interactuar con los orquestadores, la función tiene que incluir un enlace de entrada DurableClient
. El cliente se utiliza para iniciar una orquestación. También puede ayudarle a devolver una respuesta HTTP que contiene las direcciones URL para comprobar el estado de la nueva orquestación.
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 interactuar con los orquestadores, la función tiene que incluir un enlace 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);
};
Para obtener un objeto DurableOrchestrationClient
, use df.getClient
. El cliente se utiliza para iniciar una orquestación. También puede ayudarle a devolver una respuesta HTTP que contiene las direcciones URL para comprobar el estado de la nueva orquestación.
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 administrar e interactuar con los orquestadores, la función necesita un enlace de entrada durableClient
. Este enlace debe especificarse en el argumento extraInputs
al registrar la función. Se puede obtener una entrada durableClient
llamando a df.input.durableClient()
.
Para obtener un objeto DurableClient
, use df.getClient
. El cliente se utiliza para iniciar una orquestación. También puede ayudarle a devolver una respuesta HTTP que contiene las direcciones URL para comprobar el estado de la nueva orquestación.
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 interactuar con los orquestadores, la función tiene que incluir un enlace 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)
Utilice el constructor DurableOrchestrationClient
para obtener un cliente de Durable Functions. El cliente se utiliza para iniciar una orquestación. También puede ayudarle a devolver una respuesta HTTP que contiene las direcciones URL para comprobar el estado de la nueva orquestación.
Ejecución del ejemplo
Para ejecutar la orquestación de E1_HelloSequence
, envíe la siguiente solicitud POST HTTP a la función HttpStart
.
POST http://{host}/orchestrators/E1_HelloSequence
Nota
El fragmento de código HTTP anterior da por supuesto que hay una entrada en el archivo host.json
que elimina el prefijo api/
predeterminado de todas las direcciones URL de las funciones de activación de HTTP. Puede encontrar el marcador para esta configuración en el archivo host.json
en los ejemplos.
Por ejemplo, si ejecuta el ejemplo en una aplicación de función llamada "myfunctionapp", reemplace "{host}" por "myfunctionapp.azurewebsites.net".
El resultado es una respuesta HTTP 202, similar a la siguiente (acortada para simplificar):
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...)
En este punto, la orquestación se pone en cola y comienza a ejecutarse inmediatamente. La dirección URL en el encabezado Location
puede usarse para comprobar el estado de la ejecución.
GET http://{host}/runtime/webhooks/durabletask/instances/96924899c16d43b08a536de376ac786b?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
El resultado es el estado de la orquestación. Se ejecuta y se completa con rapidez, por lo que se muestra con el estado Completed (Completado) con una respuesta similar a la siguiente (acortada para simplificar):
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 puede ver, el valor de runtimeStatus
de la instancia es Completed (Completado) y output
contiene el resultado serializado con JSON de la ejecución de la función de orquestador.
Nota
Puede implementar una lógica de inicio similar para otros tipos de desencadenadores, como queueTrigger
, eventHubTrigger
o timerTrigger
.
Observe los registros de ejecución de la función. La función E1_HelloSequence
se ha iniciado y completado varias veces debido al comportamiento de reproducción descrito en el tema sobre la confiabilidad de la orquestación. Por otro lado, solo ha habido tres de ejecuciones de E1_SayHello
, puesto que esas ejecuciones de función no se reproducen.
Pasos siguientes
En este ejemplo se ha demostrado una orquestación de encadenamiento de función simple. En el ejemplo siguiente se muestra cómo implementar el de distribución ramificada de salida y entrada.