Цепочкой функций называют схему выполнения последовательности функций в определенном порядке. Часто требуется передать выходные данные одной функции во входные данные другой. В этой статье описывается последовательность цепочки, созданная при выполнении краткого руководства по Устойчивые функции (C#, JavaScript, TypeScript, Python, PowerShell или Java). Дополнительные сведения об Устойчивых функций см. в статье, посвященной обзору Устойчивых функций.
Необходимые компоненты
Примечание.
Общедоступна версия 4 модели программирования Node.js для Функции Azure. Новая модель версии 4 предназначена для более гибкого и интуитивно понятного интерфейса для разработчиков JavaScript и TypeScript. Дополнительные сведения о различиях между версиями 3 и 4 см. в руководстве по миграции.
В следующих фрагментах кода JavaScript (PM4) обозначает модель программирования версии 4, новый интерфейс.
Функции
В этой статье описаны следующие функции в примере приложения:
E1_HelloSequence
. Функция оркестратора, вызывающая E1_SayHello
несколько раз подряд. При этом сохраняются выходные данные каждого вызова E1_SayHello
и записываются результаты.
E1_SayHello
. Функция действия, которая добавляет Hello в начало строки.
HttpStart
: Функция долговременного клиента, активируемая по протоколу HTTP, которая запускает экземпляр оркестратора.
Функция оркестратора 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;
}
Для всех функций оркестратора на C# должен использоваться параметр типа DurableOrchestrationContext
, который присутствует в сборке Microsoft.Azure.WebJobs.Extensions.DurableTask
. Этот объект контекста позволяет вызывать другие функции действия и передавать им входные параметры с помощью метода CallActivityAsync
.
Этот код трижды последовательно вызывает E1_SayHello
с разными значениями параметров. Значение, возвращаемое при каждом вызове, добавляется в список outputs
, который возвращается в качестве результата функции.
function.json
Если вы используете для разработки Visual Studio Code или портал Azure, файл function.json с указанным здесь содержимым позволит создать функцию оркестратора. Для большинства функций оркестратора файл function.json будет выглядеть почти так же.
{
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
],
"disabled": false
}
Самое важное здесь — это тип привязки orchestrationTrigger
. Во всех функциях оркестратора должен использоваться такой тип триггера.
Предупреждение
Чтобы не нарушать требование "без ввода и вывода", применяемое к функциям оркестратора, не используйте никакие привязки входных или выходных данных совместно с триггером привязки orchestrationTrigger
. Если вам нужны входные или выходные привязки, используйте их в контексте функций activityTrigger
, которые вызываются оркестратором. Дополнительные сведения см. в статье об ограничениях кода функции оркестратора.
index.js
Функция оркестратора:
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;
});
Все функции оркестратора на JavaScript должны содержать модуль durable-functions
. Это библиотека, позволяющая записывать Устойчивые функции на языке JavaScript. Есть три существенных различия между функцией оркестратора и другими функциями JavaScript:
- Функция оркестратора является функцией генератора.
- Эта функция упаковывается в вызов к методу
orchestrator
модуля durable-functions
(в нашем примере это df
).
- Функции должны быть синхронными. Так как метод orchestrator обрабатывает окончательный вызов аргумента context.done, функция должна возвращать значение.
Объект context
содержит объект контекста надежной оркестровки df
, что позволяет вызывать другие функции действий и передавать им входные параметры с помощью метода callActivity
. Этот код вызывает E1_SayHello
три раза подряд с разными значениями параметров, указывая с помощью yield
, что процесс выполнения должен ожидать возврата из асинхронных функций действий. Значение, возвращаемое при каждом вызове, добавляется в массив outputs
, который возвращается при завершении функции.
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;
});
Все функции оркестратора на JavaScript должны содержать модуль durable-functions
. Этот модуль позволяет писать Устойчивые функции в JavaScript. Чтобы использовать модель программирования узлов версии 4, необходимо установить предварительную v3.x
версию durable-functions
.
Существует два существенных различия между функцией оркестратора и другими функциями JavaScript:
- Функция оркестратора является функцией генератора.
- Функции должны быть синхронными. Функция должна просто вернуться.
Объект context
содержит объект контекста надежной оркестровки df
, что позволяет вызывать другие функции действий и передавать им входные параметры с помощью метода callActivity
. Этот код вызывает sayHello
три раза подряд с разными значениями параметров, указывая с помощью yield
, что процесс выполнения должен ожидать возврата из асинхронных функций действий. Значение, возвращаемое при каждом вызове, добавляется в массив outputs
, который возвращается при завершении функции.
Примечание.
Устойчивые функции Python доступны только для среды выполнения Функции Azure 3.0.
function.json
Если вы используете для разработки Visual Studio Code или портал Azure, файл function.json с указанным здесь содержимым позволит создать функцию оркестратора. Для большинства функций оркестратора файл function.json будет выглядеть почти так же.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
Самое важное здесь — это тип привязки orchestrationTrigger
. Во всех функциях оркестратора должен использоваться такой тип триггера.
Предупреждение
Чтобы не нарушать требование "без ввода и вывода", применяемое к функциям оркестратора, не используйте никакие привязки входных или выходных данных совместно с триггером привязки orchestrationTrigger
. Если вам нужны входные или выходные привязки, используйте их в контексте функций activityTrigger
, которые вызываются оркестратором. Дополнительные сведения см. в статье об ограничениях кода функции оркестратора.
__init__.py
Функция оркестратора:
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)
Все функции оркестрации в Python должны включать durable-functions
пакет. Это библиотека, позволяющая записывать Устойчивые функции на языке Python. Есть два существенных различия между функцией оркестратора и другими функциями Python:
- Функция оркестратора является функцией генератора.
- Файл должен зарегистрировать функцию оркестратора в качестве оркестратора, указывая
main = df.Orchestrator.create(<orchestrator function name>)
в конце файла. Это помогает отличать ее от других, вспомогательных функций, объявленных в файле.
Этот объект context
позволяет вызывать другие функции действия и передавать им входные параметры с помощью метода call_activity
. Этот код вызывает E1_SayHello
три раза подряд с разными значениями параметров, указывая с помощью yield
, что процесс выполнения должен ожидать возврата из асинхронных функций действий. Значение, возвращаемое при каждом вызове, возвращается при завершении функции.
Функция действия E1_SayHello
[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] IDurableActivityContext context)
{
string name = context.GetInput<string>();
return $"Hello {name}!";
}
Действия используют атрибут ActivityTrigger
. Используйте предоставленный IDurableActivityContext
для выполнения действий, связанных с операциями, например для доступа к входному значению с помощью GetInput<T>
.
Функция E1_SayHello
реализует достаточно простую операцию форматирования строки.
Вместо привязки к IDurableActivityContext
можно выполнить прямую привязку к типу, передаваемому в функцию действия. Например:
[FunctionName("E1_SayHello_DirectInput")]
public static string SayHelloDirectInput([ActivityTrigger] string name)
{
return $"Hello {name}!";
}
E1_SayHello/function.json
Файл Function.json для функции действия E1_SayHello
будет таким же, как и для E1_HelloSequence
, с одним исключением: в нем используется тип привязки activityTrigger
вместо orchestrationTrigger
.
{
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
],
"disabled": false
}
Примечание.
Для любой функции действия, которую вызывает функция оркестрации, должна использоваться привязка activityTrigger
.
Функция E1_SayHello
реализует достаточно простую операцию форматирования строки.
E1_SayHello/index.js
module.exports = function (context) {
context.done(null, `Hello ${context.bindings.name}!`);
};
В отличие от функции оркестрации, функция действия не требует дополнительной настройки. Входные данные, которые ей передает функция оркестратора, размещаются в объекте context.bindings
под именем привязки activityTrigger
(в нашем примере это context.bindings.name
). Имя привязки можно задать в качестве параметра экспортируемой функции и обращаться к нему напрямую, как показано в этом примере кода.
Функция sayHello
реализует достаточно простую операцию форматирования строки.
const df = require("durable-functions");
const helloActivityName = "sayHello";
df.app.activity(helloActivityName, {
handler: function (input) {
return `Hello ${input}`;
},
});
В отличие от функции оркестрации, функция действия не требует дополнительной настройки. Входные данные, переданные функцией оркестратора, являются первым аргументом функции. Второй аргумент — это контекст вызова, который не используется в этом примере.
E1_SayHello/function.json
Файл Function.json для функции действия E1_SayHello
будет таким же, как и для E1_HelloSequence
, с одним исключением: в нем используется тип привязки activityTrigger
вместо orchestrationTrigger
.
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "name",
"type": "activityTrigger",
"direction": "in"
}
]
}
Примечание.
Для любой функции действия, которую вызывает функция оркестрации, должна использоваться привязка activityTrigger
.
Функция E1_SayHello
реализует достаточно простую операцию форматирования строки.
E1_SayHello/__init__.py
def main(name: str) -> str:
return f"Hello {name}!"
В отличие от функции оркестратора, для функции действия не требуется специальной настройки. Входные данные, передаваемые ей функцией оркестратора, напрямую доступны в качестве параметра функции.
Функция клиента HttpStart
Экземпляр функции оркестратора можно запустить с помощью функции клиента. HttpStart
Для запуска экземпляров E1_HelloSequence
будет использоваться функция, активируемая HTTP.
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);
}
}
Для взаимодействия с оркестраторами функция должна включать входную привязку DurableClient
. Для запуска оркестрации используется клиент. Он также может помочь вернуть ответ HTTP, содержащий URL-адреса для проверки состояния новой оркестрации.
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
}
Для взаимодействия с оркестраторами функция должна включать входную привязку 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);
};
Для получения объекта DurableOrchestrationClient
используйте командлет df.getClient
. Для запуска оркестрации используется клиент. Он также может помочь вернуть ответ HTTP, содержащий URL-адреса для проверки состояния новой оркестрации.
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);
},
});
Для управления оркестраторами и взаимодействия с ней требуется входная durableClient
привязка. Эта привязка должна быть указана в аргументе extraInputs
при регистрации функции. Входные durableClient
данные можно получить путем вызова df.input.durableClient()
.
Для получения объекта DurableClient
используйте командлет df.getClient
. Для запуска оркестрации используется клиент. Он также может помочь вернуть ответ HTTP, содержащий URL-адреса для проверки состояния новой оркестрации.
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"
}
]
}
Для взаимодействия с оркестраторами функция должна включать входную привязку 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)
Используйте конструктор DurableOrchestrationClient
для получения клиента Устойчивых функций. Для запуска оркестрации используется клиент. Он также может помочь вернуть ответ HTTP, содержащий URL-адреса для проверки состояния новой оркестрации.
Запуск примера
Чтобы запустить оркестрацию E1_HelloSequence
, отправьте указанный ниже запрос HTTP POST в функцию HttpStart
.
POST http://{host}/orchestrators/E1_HelloSequence
Примечание.
В предыдущем фрагменте HTTP предполагается, что в файле host.json
существует запись, которая позволяет удалить префикс api/
по умолчанию из всех URL-адресов функций для триггеров HTTP. Разметку для этой конфигурации можно найти в файле host.json
в примерах.
Скажем, если пример выполняется в приложении-функции с именем myfunctionapp, замените {host} значением myfunctionapp.azurewebsites.net.
Вы получите примерно такой ответ HTTP с кодом 202 (фрагмент ответа):
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...)
Функция оркестратора помещается в очередь и немедленно передается на выполнение. URL-адрес из заголовка Location
позволяет проверить состояние выполнения функции.
GET http://{host}/runtime/webhooks/durabletask/instances/96924899c16d43b08a536de376ac786b?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
В ответе на такой запрос возвращаются сведения о состоянии оркестрации. Функция выполняется и завершается достаточно быстро, поэтому, вероятнее всего, отобразится состояние Завершено в ответе примерно такого вида (фрагмент ответа):
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"}
Как видите, параметр runtimeStatus
для экземпляра имеет значение Завершено, а output
содержит результат выполнения функции оркестратора, сериализованный в формате JSON.
Примечание.
Вы можете использовать аналогичную логику запуска и для других типов триггеров, например queueTrigger
, eventHubTrigger
или timerTrigger
.
Просмотрите журналы выполнения функции. Функция E1_HelloSequence
была запущена и завершена несколько раз в соответствии с логикой повторов, описанной в разделе о надежности оркестрации. С другой стороны, функция E1_SayHello
выполнялась только три раза, поскольку для таких процессов логика повторов не применяется.
Следующие шаги
В этом примере показана простая оркестрация с цепочкой функций. В приведенном ниже примере кода показана реализация шаблона развертывания и объединения.