Поделиться через


Ограничения кода функции Orchestrator

Создание приложений с отслеживанием состояния с помощью Durable Functions. Это расширение Azure Functions. Используйте функцию orchestrator для координации других долговечных функций в приложении-функции. Функции Оркестратора — это функции с отслеживанием состояния, надежные, и они создаются для длительного выполнения.

Создавайте рабочие процессы с состоянием и отказоустойчивостью с помощью пакетов SDK Durable Task в .NET, Python и Java. Используйте оркестратор для координации действий и вложенных оркестраций. Оркестраторы с сохранением состояния, надежны и спроектированы для длительной работы.

Ограничения кода Orchestrator

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

Оркестраторы используют ресурсы событий для обеспечения надежного выполнения и поддержания состояния локальной переменной. Поведение воспроизведения кода оркестратора создает ограничения на тип кода, который можно написать в оркестраторе. Например, оркестраторы должны быть детерминированными: оркестратор воспроизводит несколько раз, и каждый раз он должен производить один и тот же результат.

Использование детерминированных API

Ниже приведены некоторые простые рекомендации, которые помогут убедиться, что код детерминирован.

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

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

Ниже приведены некоторые простые рекомендации, которые помогут убедиться, что код детерминирован.

Вызывайте API из целевых языков в оркестраторах, а используйте только детерминированные API. Детерминированный API всегда возвращает одинаковое значение для одного и того же входного значения, независимо от того, когда или как часто он вызывается.

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

Замечание

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

Даты и время

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

Не используйте DateTime.Now, DateTime.UtcNow или эквивалентные API для получения текущего времени. Такие классы, как Stopwatch следует также избегать. Для .NET функций оркестратора процесса используйте свойство IDurableOrchestrationContext.CurrentUtcDateTime, чтобы получить текущее время. Для .NET изолированных функций оркестратора используйте свойство TaskOrchestrationContext.CurrentDateTimeUtc, чтобы получить текущее время.

DateTime startTime = context.CurrentUtcDateTime;
// do some work
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);

API на основе времени не являются детерминированными и никогда не должны использоваться в оркестраторах. Каждый повтор оркестратора генерирует другое значение. Вместо этого используйте эквивалентный API для получения текущей даты или времени в Durable Task SDK, который сохраняет согласованность во время повторных воспроизведений.

Не используйте DateTime.Now, DateTime.UtcNow или эквивалентные API для получения текущего времени. Такие классы, как Stopwatch следует также избегать. TaskOrchestrationContext.CurrentUtcDateTime Используйте свойство, чтобы получить текущее время.

using Microsoft.DurableTask;

public class TimerExample : TaskOrchestrator<object?, TimeSpan>
{
    public override async Task<TimeSpan> RunAsync(TaskOrchestrationContext context, object? input)
    {
        // Use context.CurrentUtcDateTime instead of DateTime.Now or DateTime.UtcNow
        DateTime startTime = context.CurrentUtcDateTime;

        // do some work
        await context.CallActivityAsync("DoWork", null);

        TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);
        return totalTime;
    }
}

Идентификаторы GUID и UUID

API, возвращающие случайный ИДЕНТИФИКАТОР GUID или UUID, являются недетерминированными, так как созданное значение отличается для каждого воспроизведения. В зависимости от языка может быть доступен встроенный API для создания детерминированных идентификаторов GUID или UUID. В противном случае используйте функцию действия для возврата случайно созданного GUID или UUID.

Вместо ТАКИх API Guid.NewGuid()используйте API контекстного NewGuid() объекта для создания случайного GUID, безопасного для воспроизведения оркестратора.

Guid randomGuid = context.NewGuid();

Замечание

Идентификаторы GUID, созданные с помощью API контекста оркестрации, — это UUID типа 5.

API, возвращающие случайный ИДЕНТИФИКАТОР GUID или UUID, являются недетерминированными, так как созданное значение отличается для каждого воспроизведения. В зависимости от языка может быть доступен встроенный API для создания детерминированных идентификаторов GUID или UUID. В противном случае используйте действие для возврата случайно созданного GUID или UUID.

Вместо ТАКИх API Guid.NewGuid()используйте API контекстного NewGuid() объекта для создания случайного GUID, безопасного для воспроизведения оркестратора.

using Microsoft.DurableTask;

public class GuidExample : TaskOrchestrator<object?, Guid>
{
    public override async Task<Guid> RunAsync(TaskOrchestrationContext context, object? input)
    {
        // Use context.NewGuid() instead of Guid.NewGuid()
        Guid randomGuid = context.NewGuid();
        return randomGuid;
    }
}

Замечание

Идентификаторы GUID, созданные с помощью API контекста оркестрации, — это UUID типа 5.

Случайные числа

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

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

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

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

Привязки

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

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

Статические переменные

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

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

Замечание

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

Переменные среды

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

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

Сеть и HTTP

Используйте функции действий для выполнения исходящих сетевых вызовов. Если необходимо выполнить HTTP-вызов из функции оркестратора, можно также использовать устойчивые API HTTP.

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

API-интерфейсы блокировки потоков

Блокировка API-интерфейсов, таких как sleep, может вызвать проблемы с производительностью и масштабированием для функций оркестратора и повлечь за собой ненужные затраты времени выполнения по тарифному плану потребления Azure Functions. Используйте альтернативные варианты, когда они доступны. Например, используйте устойчивые таймеры для создания задержек, которые безопасны для повторного воспроизведения и не учитываются во времени выполнения оркестратора.

Блокировка API- интерфейсов, таких как "спящий режим", может привести к проблемам производительности и масштабирования для оркестраторов и следует избежать. Используйте устойчивые таймеры для создания задержек, безопасных для воспроизведения.

Используйте context.CreateTimer() вместо Task.Delay() или Thread.Sleep().

// Don't use Task.Delay() or Thread.Sleep()
// Use context.CreateTimer() instead
await context.CreateTimer(context.CurrentUtcDateTime.AddMinutes(5), CancellationToken.None);

Асинхронные API

Код оркестратора никогда не должен запускать асинхронную операцию, за исключением операций, определенных объектом контекста триггера оркестрации. Например, никогда не используйте Task.Run, Task.Delay и HttpClient.SendAsync в .NET или setTimeout и setInterval в JavaScript. Функция оркестратора должна запланировать только асинхронную работу, используя Durable SDK API, например, планируя функции активности. Любые другие асинхронные вызовы должны выполняться внутри функций действий.

Код оркестратора никогда не должен запускать асинхронную операцию, за исключением операций, определенных объектом контекста оркестрации. Например, никогда не используйте Task.Run, Task.Delay и HttpClient.SendAsync в .NET. Оркестратор должен планировать только асинхронные операции, используя API Durable Task SDK, такие как планирование действий. Любой другой тип асинхронных вызовов должен выполняться внутри действий.

Асинхронные функции JavaScript

Объявите функции оркестратора JavaScript как синхронные функции генератора. Не объявляйте функции оркестратора JavaScript как async, потому что среда выполнения Node.js не гарантирует детерминированное поведение функций async.

Python корутины

Не объявляйте функции оркестратора Python как корутины. Не используйте ключевое слово async, так как семантика корутин не совместима с моделью воспроизведения в Durable Functions. Объявляйте функции оркестратора Python как генераторы и используйте yield вместо await с API context.

Вы не должны объявлять Python оркестраторы как корутины. Иными словами, никогда не объявляйте Python оркестраторы с ключевым словом async, так как семантика корутин не соответствует модели воспроизведения "Durable Task". Всегда следует объявлять оркестраторы Python в качестве генераторов, что означает, что при вызове API контекста следует использовать yield вместо await.

from durabletask import task

# CORRECT - use yield (generator function)
def my_orchestrator(ctx: task.OrchestrationContext, input: str):
    result = yield ctx.call_activity(my_activity, input=input)
    return result

# WRONG - don't use async/await
async def bad_orchestrator(ctx: task.OrchestrationContext, input: str):
    result = await ctx.call_activity(my_activity, input=input)  # This won't work!
    return result

API потоков .NET

Платформа устойчивых задач выполняет код оркестратора в одном потоке и не может взаимодействовать с другими потоками. Выполнение асинхронных продолжений в потоке рабочего пула в выполнении оркестрации может привести к недетерминированному выполнению или взаимоблокировкам. По этой причине функции оркестратора почти никогда не должны использовать потоковые API. Например, никогда не используйте ConfigureAwait(continueOnCapturedContext: false) в функции оркестратора, чтобы гарантировать, что продолжения задач выполняются в исходной SynchronizationContext функции оркестратора.

Замечание

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

Платформа устойчивых задач выполняет код оркестратора в одном потоке и не может взаимодействовать с другими потоками. Выполнение асинхронных продолжений в потоке рабочего пула в выполнении оркестрации может привести к недетерминированному выполнению или взаимоблокировкам. По этой причине оркестраторы почти никогда не должны использовать API потоков. Например, никогда не используется ConfigureAwait(continueOnCapturedContext: false) в оркестраторе для обеспечения продолжения задач, выполняемых в исходном экземпляре SynchronizationContextоркестратора.

Замечание

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

Управление версиями

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

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

Устойчивые задачи

Замечание

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

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

Список объектов TaskCompletionSource в .NET управляет этими устойчивыми задачами внутри системы. Во время воспроизведения код оркестратора создает эти задачи. Диспетчер завершает их по мере перечисления соответствующих событий истории.

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

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

Дополнительные сведения о том, как платформа устойчивых задач выполняет функции оркестратора, см. в исходном коде Durable Task в GitHub. В частности, см. раздел TaskOrchestrationExecutor.cs и TaskOrchestrationContext.cs.

Задачи, которые могут безопасно ждать в оркестраторах, иногда называются устойчивыми задачами. Платформа устойчивых задач создает и управляет этими задачами. Примеры включают задачи, возвращаемые CallActivityAsync, WaitForExternalEvent и CreateTimer в оркестраторах .NET.

Список объектов TaskCompletionSource в .NET управляет этими устойчивыми задачами внутри системы. Во время воспроизведения код оркестратора создает эти задачи. Диспетчер завершает их по мере перечисления соответствующих событий истории.

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

Это поведение во время выполнения объясняет, почему оркестратор не может использовать await или yield в недолговечной задаче. Поток диспетчера не может ждать завершения задачи, и обратные вызовы из этой задачи могут нарушить отслеживание состояния оркестратора. Среда выполнения включает проверки для обнаружения этих нарушений.

Дополнительные сведения о том, как фреймворк Durable Task выполняет оркестраторы, см. в исходном коде Durable Task на GitHub. В частности, см. раздел TaskOrchestrationExecutor.cs и TaskOrchestrationContext.cs.

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