Устойчивые оркестрации

Устойчивая оркестрация использует функцию оркестратора для координации выполнения других функций в надежном длительном рабочем процессе, который полностью определен в коде. Функции Оркестратора имеют следующие характеристики:

  • Они определяют рабочие процессы с помощью процедурного кода. Декларативные схемы или конструкторы не требуются.
  • Они вызывают другие функции синхронно и асинхронно. Вы можете сохранять выход из вызываемых функций в локальные переменные.
  • Они автоматически сохраняют контрольную точку хода выполнения, когда функция использует оператор await или yield, чтобы процесс не терял локальное состояние при перезапуске или перезагрузке виртуальной машины.
  • Они поддерживают длительные процессы. Общее время существования экземпляра оркестрации может составлять секунды, дни или месяцы, либо вы можете настроить экземпляр так, чтобы он никогда не заканчивался.

В этой статье представлен обзор устойчивых оркестровок, включая идентификацию оркестрации, событийное хранилище, историю выполнения и распространенные шаблоны рабочих процессов, такие как вложенные оркестрации, устойчивые таймеры и обработка ошибок.

Сведения о типах функций, доступных в приложении Устойчивые функции, см. в статье Durable Task programming model.

Подсказка

Если вы используете C# с изолированной рабочей моделью .NET, вы можете писать оркестрации либо с помощью функционального подхода (статические методы с [Function] атрибутами), либо классового подхода (классы, наследующие от TaskOrchestrator<TInput, TOutput>). Для подхода на основе классов требуется пакет генератора источников Microsoft.DurableTask.Generators и обеспечивает строго типизированные вызовы. Дополнительные сведения см. в разделе "Генераторы источников" и синтаксис на основе классов. Примеры кода C# в этой статье показывают оба подхода.

Пакеты SDK для устойчивых задач предоставляют те же возможности оркестратора, что и устойчивые функции для создания надежных, длительных рабочих процессов с параллельной обработкой и координацией на основе событий. В отличие от устойчивых функций, оркестрации пакета SDK устойчивых задач выполняются как автономные приложения, поддерживаемые планировщиком устойчивых задач.

Идентификация оркестрации

Каждый экземпляр оркестрации имеет идентификатор экземпляра , также известный как идентификатор экземпляра. По умолчанию каждый идентификатор экземпляра является автоматически созданным глобально уникальным идентификатором (GUID). Однако вы можете использовать любое пользовательское значение строки в качестве идентификатора экземпляра. Каждый идентификатор экземпляра оркестрации должен быть уникальным в узле задач.

Следующие правила применяются к идентификаторам экземпляров:

  • Они должны быть от 1 до 100 символов.
  • Они не должны начинаться с @.
  • Они не должны содержать /, \#или ? символы.
  • Они не должны содержать символы элемента управления.

Замечание

По возможности используйте автоматически созданные идентификаторы экземпляров. Используйте созданные пользователем идентификаторы экземпляров в сценариях, где между экземпляром оркестрации и внешней сущностью, специфичной для приложения, например заказом на покупку или документом, существует взаимно-однозначное соответствие.

Замечание

Фактическое применение правил ограничения символов может отличаться в зависимости от поставщика хранилища , используемого приложением. Чтобы обеспечить правильное поведение и совместимость, следуйте приведенным выше правилам идентификатора экземпляра.

Идентификатор экземпляра оркестрации является обязательным параметром для большинства операций управления экземплярами. Идентификаторы экземпляров также важны для диагностирования. Например, они используются при поиске по данным отслеживания оркестрации в Application Insights для устранения неполадок или анализа. По этой причине сохраните идентификаторы созданных экземпляров во внешнем расположении, например в базе данных или логах приложений, для удобства дальнейшего обращения к ним.

Идентификатор экземпляра оркестрации является обязательным параметром для большинства операций управления экземплярами. Идентификаторы экземпляров также важны для диагностики, поэтому сохраняйте идентификаторы созданных экземпляров во внешнем расположении, что облегчает их поиск позже, например, в базе данных или журналах приложений.

Reliability

Функции оркестратора используют шаблон проектирования источник событий для надежного поддержания состояния их выполнения. Вместо прямого хранения текущего состояния оркестрации фреймворк Durable Task использует хранилище с добавлением для записи полной последовательности действий, выполняемых оркестрацией функции. Хранилище только для добавления имеет множество преимуществ по сравнению с дампингом полного состояния среды выполнения. Преимущества включают повышение производительности, масштабируемости и реагирования. Кроме того, вы получаете отложенную согласованность для транзакционных данных, полные журналы аудита и историю. Журналы аудита обеспечивают надежные компенсирующие действия.

Платформа устойчивых задач прозрачно использует ресурсы событий. За кулисами функция оркестратора использует оператор await в C# и оператор yield в JavaScript и Python. Эти операторы возвращают управление потоком оркестратора обратно в диспетчер платформы устойчивых задач. В Java вызов .await() в задаче возвращает управление диспетчеру через пользовательский экземпляр Throwable. Затем диспетчер сохраняет в хранилище любые новые действия, запланированные функцией оркестрации. Примеры действий включают вызов одной или нескольких дочерних функций или планирование устойчивого таймера. Прозрачное действие фиксации обновляет журнал выполнения экземпляра оркестрации, добавляя все новые события в хранилище, как журнал только для добавления. Аналогичным образом действие фиксации создает сообщения в хранилище для расписания выполнения фактической работы. На этом этапе функцию оркестратора можно выгрузить из памяти.

Вы можете использовать любой из доступных поставщиков хранилищ в качестве хранилища состояния среды выполнения.

Когда функция оркестрации приобретает больше работы (например, получено сообщение с ответом или истекает срок действия устойчивого таймера), оркестратор просыпается и повторно выполняет всю функцию с начала, чтобы восстановить локальное состояние. Во время воспроизведения, если код пытается вызвать функцию (или выполнить любую другую асинхронную работу), Durable Task Framework обращается к журналу выполнения текущей оркестрации. Если он обнаружит, что действие уже выполнено и дало результат, он воспроизводит результат этой функции, а код оркестратора продолжает выполняться. Воспроизведение продолжается, пока код функции полностью не выполнится или пока не будет запланирована новая асинхронная работа.

Замечание

Чтобы шаблон воспроизведения работал правильно и надежно, код функции оркестратора должен быть детерминированным. Недетерминированный код оркестратора может привести к ошибкам среды выполнения или другому непредвиденному поведению. Дополнительные сведения об ограничениях кода для функций оркестратора см. в разделе Ограничения кода функции Orchestrator.

Замечание

Если функция оркестратора выдает сообщения журнала, поведение воспроизведения может привести к возникновению повторяющихся сообщений журнала. Сведения о том, почему такое поведение происходит и как обойти его, см. в разделе «Безопасное для повторного воспроизведения ведение журнала».

История оркестрации

Поведение с использованием событий в платформе Durable Task тесно связано с кодом функции оркестратора, который вы пишете. Предположим, что у вас есть функция оркестратора с цепочкой действий, как показано в следующем примере.

Изолированная рабочая модель
[Function("HelloCities")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var outputs = new List<string>();

    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));

    // Return ["Hello Tokyo!", "Hello Seattle!", "Hello London!"].
    return outputs;
}

Модель на основе классов (изолированная рабочая роль)

Подход на основе классов использует генератор источника и требует пакета NuGet Microsoft.DurableTask.Generators .

using Microsoft.DurableTask;

[DurableTask]
public class HelloCities : TaskOrchestrator<object?, List<string>>
{
    public override async Task<List<string>> RunAsync(
        TaskOrchestrationContext context, object? input)
    {
        var outputs = new List<string>();

        outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
        outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
        outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));

        // Return ["Hello Tokyo!", "Hello Seattle!", "Hello London!"].
        return outputs;
    }
}

Модель внутрипроцессного процесса
[FunctionName("HelloCities")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var outputs = new List<string>();

    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
    outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));

    // Return ["Hello Tokyo!", "Hello Seattle!", "Hello London!"].
    return outputs;
}
using Microsoft.DurableTask;

[DurableTask]
public class HelloCities : TaskOrchestrator<object?, List<string>>
{
    public override async Task<List<string>> RunAsync(TaskOrchestrationContext context, object? input)
    {
        var outputs = new List<string>();

        outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
        outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
        outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));

        return outputs;
    }
}

Каждый раз, когда вы планируете выполнение функции активности, Durable Task Framework сохраняет состояние выполнения функции в различных контрольных точках. На каждой контрольной точке фреймворк сохраняет состояние в надёжном хранилище. Это состояние — история оркестровки.

Историческая таблица

На каждой контрольной точке Рамка устойчивых задач выполняет следующие действия:

  • Сохраняет историю выполнения в постоянное хранилище.
  • Помещает сообщения в очередь для функций, которые оркестратор хочет вызвать.
  • Помещает в очередь сообщения для самого оркестратора, например, сообщения о долговременных таймерах.

Когда завершается контрольная точка, фреймворк удаляет функцию оркестратора из памяти, пока для неё не появится новая работа.

Замечание

служба хранилища Azure не предоставляет никаких гарантий согласованности данных между хранилищем таблиц и очередями при сохранении данных. Для обработки сбоев Устойчивые функции служба хранилища Azure провайдер использует паттерны консистентности, такие как разделение ответственности команд и запросов (CQRS). Альтернативные поставщики хранения обеспечивают более строгие гарантии стабильности:

Когда функция, показанная ранее, завершится, её история будет выглядеть примерно как данные в следующей таблице в Хранилище Таблиц. Записи сокращены для иллюстрации.

PartitionKey (InstanceId) Тип события Отметка времени Ввод Имя Результат Статус
eaee885b Запуск выполнен 2021-05-05T18:45:28.852Z null HelloCities
eaee885b OrchestratorStarted 2021-05-05T18:45:32.362Z
eaee885b ЗадачаЗапланирована 2021-05-05T18:45:32.670Z Сайхелло
eaee885b OrchestratorCompleted 2021-05-05T18:45:32.670Z
eaee885b ЗадачаВыполнена 2021-05-05T18:45:34.201Z ""Hello Tokyo!""
eaee885b OrchestratorStarted 2021-05-05T18:45:34.232Z
eaee885b ЗадачаЗапланирована 2021-05-05T18:45:34.435Z Сайхелло
eaee885b OrchestratorCompleted 2021-05-05T18:45:34.435Z
eaee885b ЗадачаВыполнена 2021-05-05T18:45:34.763Z """Hello Сиэтл!""
eaee885b OrchestratorStarted 2021-05-05T18:45:34.857Z
eaee885b ЗадачаЗапланирована 2021-05-05T18:45:34.857Z Сайхелло
eaee885b OrchestratorCompleted 2021-05-05T18:45:34.857Z
eaee885b ЗадачаВыполнена 2021-05-05T18:45:34.919Z """Hello London!""
eaee885b OrchestratorStarted 2021-05-05T18:45:35.032Z
eaee885b OrchestratorCompleted 2021-05-05T18:45:35.044Z
eaee885b ВыполнениеЗавершено 2021-05-05T18:45:35.044Z ["Привет, Токио!", "Привет, Сиэтл!", "Привет, Лондон!"] Завершено

Столбцы таблицы содержат следующие значения:

  • PartitionKey: идентификатор экземпляра оркестрации.
  • EventType: тип события. Подробные описания всех типов исторических событий см. в разделе Durable Task Framework History Events.
  • Метка времени: метка координированного универсального времени исторического события.
  • Входные данные: входные данные в формате JSON функции.
  • Имя: имя вызываемой функции.
  • Результат: выходные данные функции, в частности, его возвращаемое значение.

Предупреждение

Эта таблица полезна как средство отладки, но его формат и содержимое могут измениться по мере развития расширения Устойчивые функции.

Каждый раз, когда функция возобновляется после ожидания завершения задачи, платформа устойчивых задач повторно запускает функцию оркестратора с нуля. При каждом повторном запуске он обращается к журналу выполнения, чтобы определить, завершена ли текущая асинхронная задача. Если журнал выполнения показывает, что задача уже завершена, платформа воспроизводит выходные данные этой задачи и переходит к следующей задаче. Этот процесс продолжается до тех пор, пока не будет воспроизведен весь журнал выполнения. После воспроизведения текущего журнала выполнения локальные переменные восстанавливаются до предыдущих значений.

Функции и шаблоны

В следующих разделах описываются функции и шаблоны функций оркестратора.

Вложенные оркестрации в функциях оркестратора

Функции оркестратора могут вызывать функции активности, но также могут вызывать другие функции оркестратора. Например, можно создать большую оркестрацию из библиотеки функций оркестратора. Кроме того, можно параллельно запускать несколько экземпляров функции оркестратора.

Дополнительные сведения и примеры см. в подоркестрациях устойчивых функций (Функции Azure).

Устойчивые таймеры

Оркестрации могут запланировать устойчивые таймеры для реализации задержек или настройки обработки времени ожидания для асинхронных действий. Используйте устойчивые таймеры в функциях оркестратора вместо родных API языка программирования sleep.

Дополнительные сведения и примеры см. в разделе Таймеры в Устойчивые функции (Функции Azure).

Внешние события

Функции оркестратора могут ожидать внешних событий для обновления экземпляра оркестрации. Эта функция Устойчивые функции часто полезна для обработки взаимодействия с пользователями или других внешних обратных вызовов.

Для получения дополнительной информации и примеров см. раздел Обработка внешних событий в Устойчивые функции (Функции Azure).

Обработка ошибок

Функции оркестратора могут использовать функции обработки ошибок языка программирования. Код оркестрации поддерживает существующие паттерны, такие как try/catch.

Функции оркестратора также могут добавлять политики повторных попыток в функции активности или в подчиненные функции оркестрации, которые они вызывают. Если действие или функция подоркестратора завершается ошибкой с исключением, указанная политика повторных попыток может автоматически отложить выполнение и повторить его до указанного количества раз.

Замечание

Если в функции оркестратора существует необработанное исключение, экземпляр оркестрации завершается в Failed состоянии. Нельзя перепробовать экземпляр оркестрации после неудачи.

Для получения дополнительной информации и примеров см. раздел Обработка ошибок в Устойчивые функции (Функции Azure).

Критические разделы (Устойчивые функции 2.x)

Экземпляры оркестрации являются однопоточными, поэтому условия гонки не являются проблемой в оркестрации. Однако условия гонки возможны, когда оркестрации взаимодействуют с внешними системами. Для смягчения гоночных условий при взаимодействии с внешними системами функции оркестратора могут определять критические секции с помощью блокировки. .NET и JavaScript поддерживают критические разделы.

В следующем примере кода показана функция оркестратора, определяющая критически важный раздел. Вход в критический раздел требует передачи одной или нескольких ссылок на прочный объект, который надёжно управляет состоянием замка. Только один экземпляр этого оркестрации может выполнять код в критическом разделе одновременно.

Используйте LockAsync метод для входа в критический раздел.

[FunctionName("Synchronize")]
public static async Task Synchronize(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var lockId = new EntityId("LockEntity", "MyLockIdentifier");
    using (await context.LockAsync(lockId))
    {
        // Critical section. Only one orchestration can enter at a time.
    }
}

В оркестрациях изолированного рабочего процесса .NET используйте TaskOrchestrationContext.Entities.LockEntitiesAsync (см. Сопоставление API для изолированного процесса .NET).

Метод LockAsync получает устойчивые блокировки и возвращает IDisposable, который заканчивает критическую секцию при удалении. Этот IDisposable результат можно использовать вместе с блоком using для получения синтаксического представления критического раздела. Когда функция оркестратора входит в критически важный раздел, только один экземпляр может выполнять этот блок кода. Любые другие экземпляры, которые пытаются войти в критическую секцию, блокируются до тех пор, пока предыдущий экземпляр не выйдет из критической секции.

Функция критической секции также полезна для координации изменений долговечных объектов. Дополнительные сведения о критических разделах см. в разделе "Координация сущностей".

Замечание

В Устойчивые функции 2.x критические разделы доступны для оркестраций .NET и JavaScript. Для .NET API различается в зависимости от модели: во внутрипроцессной модели используется IDurableOrchestrationContext.LockAsync, а в изолированной — TaskOrchestrationContext.Entities.LockEntitiesAsync.

Вызовы конечных точек HTTP (Устойчивые функции 2.x)

Функции оркестратора не могут выполнять операции ввода-вывода, как описано в ограничениях кода функций Orchestrator. Чтобы обойти это ограничение, любой код, необходимый для выполнения операций ввода-вывода, оберните в функцию активности. Оркестрации, взаимодействующие с внешними системами, часто используют функции активности для вызова HTTP и возврата результатов в оркестрацию.

Чтобы упростить этот распространенный шаблон, функции оркестратора могут использовать CallHttpAsync метод для вызова API-интерфейсов HTTP напрямую.

Изолированная рабочая модель
[Function("CheckSiteAvailable")]
public static async Task CheckSiteAvailable(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    Uri url = context.GetInput<Uri>();

    // Make an HTTP GET request to the specified endpoint.
    DurableHttpResponse response = await context.CallHttpAsync(
        method: HttpMethod.Get,
        uri: url,
        content: null,
        retryOptions: null);

    if ((int)response.StatusCode == 400)
    {
        // Handle error codes.
    }
}

Модель внутрипроцессного процесса
[FunctionName("CheckSiteAvailable")]
public static async Task CheckSiteAvailable(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    Uri url = context.GetInput<Uri>();

    // Make an HTTP GET request to the specified endpoint.
    DurableHttpResponse response = 
        await context.CallHttpAsync(HttpMethod.Get, url);

    if ((int)response.StatusCode == 400)
    {
        // Handle error codes.
    }
}

Помимо поддержки базовых шаблонов запросов и ответов, метод поддерживает автоматическую обработку распространённых асинхронных шаблонов опроса HTTP 202. Она также поддерживает проверку подлинности с помощью внешних служб с помощью управляемых удостоверений.

Дополнительные сведения и подробные примеры см. в разделе "Функции HTTP".

Замечание

Вызов конечных точек HTTP непосредственно из функций оркестратора доступен в Устойчивые функции 2.0 и в более поздних версиях.

Передача нескольких параметров функциям действия

Нельзя напрямую передавать несколько параметров функции активности. Вместо этого передайте массив объектов или составных объектов.

Изолированная рабочая модель

В .NET используйте сериализуемый составной тип, например запись, для передачи нескольких параметров.

public record CourseInfo(string Major, int UniversityYear);

[Function("GetCourseRecommendations")]
public static async Task<object> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    int universityYear = context.GetInput<int>();
    CourseInfo courseInfo = new("ComputerScience", universityYear);
    object courseRecommendations = await context.CallActivityAsync<object>(
        "CourseRecommendations", courseInfo);
    return courseRecommendations;
}

Модель внутрипроцессного процесса

В .NET используйте сериализуемый составной тип для передачи нескольких параметров. В следующем примере используется простой класс:

public class CourseInfo
{
    public string Major { get; set; }
    public int UniversityYear { get; set; }
}

[FunctionName("GetCourseRecommendations")]
public static async Task<object> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var input = new CourseInfo
    {
        Major = "ComputerScience",
        UniversityYear = context.GetInput<int>()
    };

    object courseRecommendations = await context.CallActivityAsync<object>(
        "CourseRecommendations",
        input);
    return courseRecommendations;
}

В .NET используйте типы записей или кортежи для передачи нескольких параметров как одного составного объекта.

using Microsoft.DurableTask;

public record LocationInfo(string City, string State);

[DurableTask]
public class GetWeatherOrchestration : TaskOrchestrator<object?, string>
{
    public override async Task<string> RunAsync(TaskOrchestrationContext context, object? input)
    {
        var location = new LocationInfo("Seattle", "WA");
        string weather = await context.CallActivityAsync<string>("GetWeather", location);
        return weather;
    }
}

Дальнейшие действия