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


Обработка ошибок в устойчивых функциях (Функции Azure)

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

Примечание.

Общедоступна версия 4 модели программирования Node.js для Функции Azure. Новая модель версии 4 предназначена для более гибкого и интуитивно понятного интерфейса для разработчиков JavaScript и TypeScript. Дополнительные сведения о различиях между версиями 3 и 4 см. в руководстве по миграции.

В следующих фрагментах кода JavaScript (PM4) обозначает модель программирования версии 4, новый интерфейс.

Ошибки в функциях активности и подоркестрациях

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

Например, рассмотрим следующую функцию оркестратора, которая выполняет передачу средств между двумя счетами:

В среде выполнения Durable Functions C# необработанные исключения выбрасываются в виде FunctionFailedException.

Сообщение об исключении обычно определяет, какие функции активности или под-оркестрации вызвали сбой. Чтобы получить более подробные сведения об ошибках, проверьте InnerException свойство.

[FunctionName("TransferFunds")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var transferDetails = context.GetInput<TransferOperation>();

    await context.CallActivityAsync("DebitAccount",
        new
        {
            Account = transferDetails.SourceAccount,
            Amount = transferDetails.Amount
        });

    try
    {
        await context.CallActivityAsync("CreditAccount",
            new
            {
                Account = transferDetails.DestinationAccount,
                Amount = transferDetails.Amount
            });
    }
    catch (FunctionFailedException)
    {
        // Refund the source account.
        // Another try/catch could be used here based on the needs of the application.
        await context.CallActivityAsync("CreditAccount",
            new
            {
                Account = transferDetails.SourceAccount,
                Amount = transferDetails.Amount
            });
    }
}

Примечание.

Предыдущие примеры C# предназначены для расширения "Устойчивые функции" версии 2.x. Для расширения Устойчивые функции 1.x необходимо использовать DurableOrchestrationContext вместо IDurableOrchestrationContext. Дополнительные сведения о различиях между версиями см. в статье Версии устойчивых функций.

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

Ошибки в функциях сущностей

Поведение обработки исключений для функций сущностей отличается в зависимости от модели размещения устойчивых функций:

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

[FunctionName("Function1")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    try
    {
        var entityId = new EntityId(nameof(Counter), "myCounter");
        await context.CallEntityAsync(entityId, "Add", 1);
    }
    catch (Exception ex)
    {
        // The exception type will be InvalidOperationException with the message "this is an entity exception".
    }
    return string.Empty;
}

[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
    switch (ctx.OperationName.ToLowerInvariant())
    {
        case "add":
            throw new InvalidOperationException("this is an entity exception");
        case "get":
            ctx.Return(ctx.GetState<int>());
            break;
    }
}

Автоматическое повторение попыток при сбое

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

[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var retryOptions = new RetryOptions(
        firstRetryInterval: TimeSpan.FromSeconds(5),
        maxNumberOfAttempts: 3);

    await context.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);

    // ...
}

Примечание.

Предыдущие примеры C# предназначены для расширения "Устойчивые функции" версии 2.x. Для расширения Устойчивые функции 1.x необходимо использовать DurableOrchestrationContext вместо IDurableOrchestrationContext. Дополнительные сведения о различиях между версиями см. в статье Версии устойчивых функций.

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

  • Max number of attempts (Максимальное число попыток): максимальное число попыток. Если задано значение 1, повторная попытка не выполняется.
  • First retry interval (Интервал до первого повтора): время ожидания перед первой повторной попыткой.
  • Backoff coefficient (Коэффициент отсрочки): коэффициент, позволяющий определить степень увеличения отсрочки. По умолчанию равен 1.
  • Max retry interval (Максимальный интервал повтора): максимальное время ожидания между повторными попытками.
  • Retry timeout (Время ожидания повтора): максимальное время, отведенное на выполнение повторных попыток. Поведение по умолчанию — бесконечное повторение.

Пользовательские обработчики повторных попыток

При использовании .NET или Java также можно реализовать обработчики повторных попыток в коде. Это полезно, если декларативные политики повторных попыток недостаточно выражены. Для языков, которые не поддерживают пользовательские обработчики повторных попыток, вы по-прежнему можете реализовать политики повторных попыток с помощью циклов, обработки исключений и таймеров для добавления задержек между повторными попытками.

RetryOptions retryOptions = new RetryOptions(
    firstRetryInterval: TimeSpan.FromSeconds(5),
    maxNumberOfAttempts: int.MaxValue)
    {
        Handle = exception =>
        {
            // True to handle and try again, false to not handle and throw.
            if (exception is TaskFailedException failure)
            {
                // Exceptions from TaskActivities are always this type. Inspect the
                // inner Exception to get more details.
            }

            return false;
        };
    }

await ctx.CallActivityWithRetryAsync("FlakeyActivity", retryOptions, null);

Время ожидания функций

Можно отменить вызов функции в функции оркестратора, если его завершение занимает слишком много времени. Правильный способ сделать это в настоящее время — создать устойчивый таймер, используя селектор задач "any", как показано в следующем примере:

[FunctionName("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    TimeSpan timeout = TimeSpan.FromSeconds(30);
    DateTime deadline = context.CurrentUtcDateTime.Add(timeout);

    using (var cts = new CancellationTokenSource())
    {
        Task activityTask = context.CallActivityAsync("FlakyFunction");
        Task timeoutTask = context.CreateTimer(deadline, cts.Token);

        Task winner = await Task.WhenAny(activityTask, timeoutTask);
        if (winner == activityTask)
        {
            // success case
            cts.Cancel();
            return true;
        }
        else
        {
            // timeout case
            return false;
        }
    }
}

Примечание.

Предыдущие примеры C# предназначены для расширения "Устойчивые функции" версии 2.x. Для расширения Устойчивые функции 1.x необходимо использовать DurableOrchestrationContext вместо IDurableOrchestrationContext. Дополнительные сведения о различиях между версиями см. в статье Версии устойчивых функций.

Примечание.

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

необработанных исключений.

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

Включите настраиваемые свойства исключений для элемента FailureDetails (.NET Isolated)

При выполнении рабочих процессов устойчивых задач в изолированной модели .NET ошибки задач автоматически сериализуются в объект FailureDetails. По умолчанию этот объект включает стандартные поля, такие как:

  • ErrorType — имя типа исключения
  • Сообщение — сообщение об исключении
  • StackTrace — сериализованная трассировка стека
  • InnerFailure — вложенный объект FailureDetails для рекурсивных внутренних исключений

Начиная с Microsoft.Azure.Functions.Worker.Extensions.DurableTask версии 1.9.0, это поведение можно расширить, реализуя IExceptionPropertiesProvider (определенный в пакете Microsoft.DurableTask.Worker начиная с версии 1.16.1). Этот поставщик определяет, какие типы исключений и какие из их свойств следует включить в словарь FailureDetails.Properties.

Примечание.

  • Эта функция доступна в только .NET Isolated. Поддержка Java будет добавлена в одном из будущих релизов.
  • Убедитесь, что вы используете Microsoft.Azure.Functions.Worker.Extensions.DurableTask версии 1.9.0 или более поздней версии.
  • Убедитесь, что вы используете Microsoft.DurableTask.Worker версии 1.16.1 или более поздней версии.

Реализация компонента для предоставления свойств исключений

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

using Microsoft.DurableTask.Worker;

public class CustomExceptionPropertiesProvider : IExceptionPropertiesProvider
{
    public IDictionary<string, object?>? GetExceptionProperties(Exception exception)
    {
        return exception switch
        {
            ArgumentOutOfRangeException e => new Dictionary<string, object?>
            {
                ["ParamName"] = e.ParamName,
                ["ActualValue"] = e.ActualValue
            },
            InvalidOperationException e => new Dictionary<string, object?>
            {
                ["CustomHint"] = "Invalid operation occurred",
                ["TimestampUtc"] = DateTime.UtcNow
            },
            _ => null // Other exception types not handled
        };
    }
}

Регистрация поставщика

Зарегистрируйте настраиваемый IExceptionPropertiesProvider в изолированном хосте рабочей роли .NET, как правило, в Program.cs.

using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder =>
    {
        // Register custom exception properties provider
        builder.Services.AddSingleton<IExceptionPropertiesProvider, CustomExceptionPropertiesProvider>();
    })
    .Build();

host.Run();

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

Пример вывода данных FailureDetails

При возникновении исключения, соответствующего конфигурации поставщика, оркестрация получает сериализованную структуру FailureDetails следующим образом:

{
  "errorType": "TaskFailedException",
  "message": "Activity failed with an exception.",
  "stackTrace": "...",
  "innerFailure": {
    "errorType": "ArgumentOutOfRangeException",
    "message": "Specified argument was out of range.",
    "properties": {
      "ParamName": "count",
      "ActualValue": 42
    }
  }
}

Следующие шаги