Шаблоны оркестрацииOrchestration patterns

Устойчивые функции упрощают создание рабочих процессов с отслеживанием состояния, состоящих из дискретных долго выполняющихся действий, в бессерверной среде.Durable Functions makes it easier to create stateful workflows that are composed of discrete, long running activities in a serverless environment. Устойчивые функции могут отслеживать ход выполнения рабочих процессов и периодически создавать контрольные точки в журнале выполнения, поэтому они предоставляют возможности реализации некоторых интересных шаблонов.Since Durable Functions can track the progress of your workflows and periodically checkpoints the execution history, it lends itself to implementing some interesting patterns.

Цепочка функцийFunction chaining

В типичном последовательном процессе действия должны выполняться один за другим в определенном порядке.In a typical sequential process, activities need to execute one after the other in a particular order. При необходимости для предстоящего действия могут потребоваться некоторые выходные данные предыдущей функции.Optionally, the upcoming activity may require some output from the previous function. Эта зависимость от порядка действий создает цепочку выполнения функций.This dependency on the ordering of activities creates a function chain of execution.

Преимущество использования Устойчивых функций для реализации этого шаблона рабочего процесса обусловлено возможностью создания контрольных точек.The benefit of using Durable Functions to implement this workflow pattern comes from its ability to do checkpointing. Если происходит аварийное завершение работы сервера, истекает время ожидания сети или возникает какая-либо другая проблема, Устойчивые функции могут возобновлять рабочий процесс на основе последнего известного состояния и продолжать выполнять его, даже если он запущен на другом сервере.If the server crashes, the network times out or some other issue occurs, Durable functions can resume from the last known state and continue running your workflow even if it's on another server.

[FunctionName("PlaceOrder")]
public static async Task<string> PlaceOrder([OrchestrationTrigger] DurableOrchestrationContext context)
{
    OrderRequestData orderData = context.GetInput<OrderRequestData>();

    await context.CallActivityAsync<bool>("CheckAndReserveInventory", orderData);
    await context.CallActivityAsync<bool>("ProcessPayment", orderData);

    string trackingNumber = await context.CallActivityAsync<string>("ScheduleShipping", orderData);
    await context.CallActivityAsync<string>("EmailCustomer", trackingNumber);

    return trackingNumber;
}

В предыдущем примере кода функция CallActivityAsync отвечает за выполнение определенного действия на виртуальной машине в центре обработки данных.In the preceding code sample, the CallActivityAsync function is responsible for running a given activity on a virtual machine in the data center. Если значение await возвращается и базовая задача завершается, выполнение записывается в таблицу журнала.When the await returns and the underlying Task completes, the execution will be recorded to the history table. Код в функции оркестратора может использовать любую из привычных конструкций библиотеки параллельных задач и ключевых слов async/await.The code in the orchestrator function can make use of any of the familiar constructs of the Task Parallel Library and the async/await keywords.

Следующий код является упрощенным примером того, как может выглядеть метод ProcessPayment:The following code is a simplified example of what the ProcessPayment method may look like:

[FunctionName("ProcessPayment")]
public static bool ProcessPayment([ActivityTrigger] DurableActivityContext context)
{
    OrderRequestData orderData = context.GetInput<OrderRequestData>();

    ApplyCoupons(orderData);
    if(IssuePaymentRequest(orderData)) {
        return true;
    }

    return false;
}

Асинхронные API HTTPAsynchronous HTTP APIs

В некоторых случаях рабочие процессы могут содержать действия, выполнение которых занимает относительно длительный период времени.In some cases, workflows may contain activities that take a relatively long period of time to complete. Представьте себе процесс, который запускает резервное копирование файлов мультимедиа в хранилище BLOB-объектов.Imagine a process that kicks off the backup of media files into blob storage. В зависимости от размера и количества файлов мультимедиа процесс резервного копирования может занять несколько часов.Depending on the size and quantity of the media files, this backup process may take hours to complete.

В этом сценарии возможность DurableOrchestrationClient проверить состояние выполняющегося рабочего процесса будет полезной.In this scenario, the DurableOrchestrationClient's ability to check the status of a running workflow becomes useful. При использовании HttpTrigger для запуска рабочего процесса можно применить метод CreateCheckStatusResponse для возврата экземпляра HttpResponseMessage.When using an HttpTrigger to start a workflow, the CreateCheckStatusResponse method can be used to return an instance of HttpResponseMessage. Этот ответ предоставляет клиенту универсальный код ресурса (URI) в полезных данных, который можно использовать для проверки состояния выполняющегося процесса.This response provides the client with a URI in the payload that can be used to check the status of the running process.

[FunctionName("OrderWorkflow")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, "POST")]HttpRequestMessage req,
    [OrchestrationClient ] DurableOrchestrationClient orchestrationClient)
{
    OrderRequestData data = await req.Content.ReadAsAsync<OrderRequestData>();

    string instanceId = await orchestrationClient.StartNewAsync("PlaceOrder", data);

    return orchestrationClient.CreateCheckStatusResponse(req, instanceId);
}

В примере ниже показана структура полезных данных ответа.The sample result below shows the structure of the response payload.

{
    "id": "instanceId",
    "statusQueryGetUri": "http://host/statusUri",
    "sendEventPostUri": "http://host/eventUri",
    "terminatePostUri": "http://host/terminateUri"
}

Используя предпочтительный HTTP-клиент, можно выполнить запросы GET к URI в statusQueryGetUri, чтобы проверить состояние выполняющегося рабочего процесса.Using your preferred HTTP client, GET requests can be made to the URI in statusQueryGetUri to inspect the status of the running workflow. Возвращенный ответ о состоянии должен напоминать следующий код.The returned status response should resemble the following code.

{
    "runtimeStatus": "Running",
    "input": {
        "$type": "DurableFunctionsDemos.OrderRequestData, DurableFunctionsDemos"
    },
    "output": null,
    "createdTime": "2018-01-01T00:22:05Z",
    "lastUpdatedTime": "2018-01-01T00:22:09Z"
}

По мере продолжения процесса ответ о состоянии изменится на Failed (Сбой) или Completed (Завершен).As the process continues, the status response will change to either Failed or Completed. При успешном завершении свойство оutput в полезных данных будет содержать возвращенные данные.On successful completion, the output property in the payload will contain any returned data.

МониторингMonitoring

Для простых повторяющихся задач решение "Функции Azure" предоставляет TimerTrigger, который можно запланировать на основе выражения CRON.For simple recurring tasks, Azure Functions provides the TimerTrigger that can be scheduled based on a CRON expression. Этот таймер хорошо подходит для простых кратковременных задач, но в некоторых случаях требуется более гибкое планирование.The timer works well for simple, short-lived tasks, but there might be scenarios where more flexible scheduling is needed. В таких случаях могут помочь Устойчивые функции и шаблон мониторинга.This scenario is when the monitoring pattern and Durable Functions can help.

Устойчивые функции обеспечивают гибкие интервалы планирования, управление временем существования и создание нескольких процессов мониторинга из одной функции оркестрации.Durable Functions allows for flexible scheduling intervals, lifetime management, and the creation of multiple monitor processes from a single orchestration function. Одним из вариантов использования этих возможностей может быть создание наблюдателей за изменением цен на акции, которые завершаются после достижения определенного порогового значения.One use case for this functionality might be to create watchers for stock price changes that complete once a certain threshold is met.

[FunctionName("CheckStockPrice")]
public static async Task CheckStockPrice([OrchestrationTrigger] DurableOrchestrationContext context)
{
    StockWatcherInfo stockInfo = context.GetInput<StockWatcherInfo>();
    const int checkIntervalSeconds = 120;
    StockPrice initialStockPrice = null;

    DateTime fireAt;
    DateTime exitTime = context.CurrentUtcDateTime.Add(stockInfo.TimeLimit);

    while (context.CurrentUtcDateTime < exitTime)
    {
        StockPrice currentStockPrice = await context.CallActivityAsync<StockPrice>("GetStockPrice", stockInfo);

        if (initialStockPrice == null)
        {
            initialStockPrice = currentStockPrice;
            fireAt = context.CurrentUtcDateTime.AddSeconds(checkIntervalSeconds);
            await context.CreateTimer(fireAt, CancellationToken.None);
            continue;
        }

        decimal percentageChange = (initialStockPrice.Price - currentStockPrice.Price) /
                               ((initialStockPrice.Price + currentStockPrice.Price) / 2);

        if (Math.Abs(percentageChange) >= stockInfo.PercentageChange)
        {
            // Change threshold detected
            await context.CallActivityAsync("NotifyStockPercentageChange", currentStockPrice);
            break;
        }

        // Sleep til next polling interval
        fireAt = context.CurrentUtcDateTime.AddSeconds(checkIntervalSeconds);
        await context.CreateTimer(fireAt, CancellationToken.None);
    }
}

Метод CreateTimer``DurableOrchestrationContext настраивает расписание следующего вызова цикла для проверки изменений цен на акции.DurableOrchestrationContext's CreateTimer method sets up the schedule for the next invocation of the loop to check for stock price changes. DurableOrchestrationContext также имеет свойство CurrentUtcDateTime, позволяющее получить текущее значение даты и времени в формате UTC.DurableOrchestrationContext also has a CurrentUtcDateTime property to get the current DateTime value in UTC. Лучше использовать это свойство вместо DateTime.UtcNow, так как оно легко имитируется для тестирования.It's better to use this property instead of DateTime.UtcNow because it's easily mocked for testing.