Udostępnij za pomocą


Obsługa błędów w rozszerzeniu Durable Functions (Azure Functions)

Orkiestracje funkcji durable są implementowane w kodzie i mogą korzystać z wbudowanych funkcji obsługi błędów języka programowania. Naprawdę nie ma żadnych nowych pojęć, których musisz nauczyć się dodawać do aranżacji obsługę błędów i rekompensatę. Istnieje jednak kilka zachowań, o których należy pamiętać.

Uwaga

Wersja 4 modelu programowania Node.js dla usługi Azure Functions jest ogólnie dostępna. Nowy model w wersji 4 został zaprojektowany z myślą o bardziej elastycznym i intuicyjnym środowisku dla deweloperów języków JavaScript i TypeScript. Dowiedz się więcej o różnicach między wersjami 3 i v4 w przewodniku migracji.

W poniższych fragmentach kodu javaScript (PM4) oznacza model programowania W wersji 4, nowe środowisko.

Błędy w funkcjach aktywności i podorchiestracjach

W usłudze Durable Functions nieobsługiwane wyjątki zgłaszane w ramach funkcji działań lub podaranżacji są marshalingowane z powrotem do funkcji orkiestratora przy użyciu ustandaryzowanych typów wyjątków.

Rozważmy na przykład następującą funkcję orkiestratora, która wykonuje transfer środków między dwoma kontami:

W Durable Functions C# nieobsługiwane wyjątki są zgłaszane jako FunctionFailedException.

Komunikat o wyjątku zazwyczaj identyfikuje, które funkcje działania lub pod-orchestracje spowodowały awarię. Aby uzyskać dostęp do bardziej szczegółowych informacji o błędach, sprawdź właściwość 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
            });
    }
}

Uwaga

Poprzednie przykłady języka C# dotyczą rozszerzenia Durable Functions 2.x. W przypadku rozszerzenia Durable Functions 1.x należy użyć funkcji DurableOrchestrationContextIDurableOrchestrationContextzamiast . Aby uzyskać więcej informacji na temat różnic między wersjami, zobacz artykuł Wersje rozszerzenia Durable Functions.

Jeśli pierwsze wywołanie funkcji CreditAccount zakończy się niepowodzeniem, funkcja orkiestratora kompensuje środki z powrotem do konta źródłowego.

Błędy w funkcjach jednostki

Zachowanie obsługi wyjątków dla funkcji jednostek różni się w zależności od modelu hostingu Durable Functions.

W usłudze Durable Functions przy użyciu języka C# w procesie oryginalne typy wyjątków zgłaszane przez funkcje jednostek są bezpośrednio zwracane do orkiestratora.

[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;
    }
}

Automatyczne ponawianie próby po awarii

Podczas wywoływania funkcji działań lub funkcji aranżacji podrzędnej można określić zasady automatycznego ponawiania. Poniższy przykład próbuje wywołać funkcję maksymalnie trzy razy i czeka 5 sekund między kolejnymi próbami:

[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);

    // ...
}

Uwaga

Poprzednie przykłady języka C# dotyczą rozszerzenia Durable Functions 2.x. W przypadku rozszerzenia Durable Functions 1.x należy użyć funkcji DurableOrchestrationContextIDurableOrchestrationContextzamiast . Aby uzyskać więcej informacji na temat różnic między wersjami, zobacz artykuł Wersje rozszerzenia Durable Functions.

Wywołanie funkcji działania w poprzednim przykładzie przyjmuje parametr do konfigurowania zasad automatycznego ponawiania. Istnieje kilka opcji dostosowywania zasad automatycznego ponawiania prób:

  • Maksymalna liczba prób: maksymalna liczba prób. Jeśli zostanie ustawiona wartość 1, nie będzie ponawiania próby.
  • Interwał ponawiania prób: czas oczekiwania przed pierwszą próbą ponawiania próby.
  • Współczynnik wycofywania: współczynnik używany do określania współczynnika wzrostu wycofywania. Wartość domyślna to 1.
  • Maksymalny interwał ponawiania prób: maksymalny czas oczekiwania między ponownymi próbami.
  • Limit czasu ponawiania prób: maksymalna ilość czasu na ponawianie prób. Domyślne zachowanie polega na ponawianiu próby przez czas nieokreślony.

Niestandardowe programy obsługi ponawiania prób

W przypadku korzystania z platformy .NET lub Języka Java możesz również zaimplementować programy obsługi ponawiania prób w kodzie. Jest to przydatne, gdy zasady ponawiania deklaratywnego nie są wystarczająco wyraziste. W przypadku języków, które nie obsługują niestandardowych procedur obsługi ponawiania prób, nadal istnieje możliwość implementowania zasad ponawiania przy użyciu pętli, obsługi wyjątków i czasomierzy w celu wstrzykiwania opóźnień między ponownymi próbami.

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);

Limity czasu funkcji

Jeśli ukończenie działania funkcji trwa zbyt długo, możesz porzucić wywołanie funkcji w funkcji orkiestratora. Właściwym sposobem, aby to zrobić dzisiaj, jest utworzenie trwałego czasomierza z selektorem zadań "any", jak w poniższym przykładzie:

[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;
        }
    }
}

Uwaga

Poprzednie przykłady języka C# dotyczą rozszerzenia Durable Functions 2.x. W przypadku rozszerzenia Durable Functions 1.x należy użyć funkcji DurableOrchestrationContextIDurableOrchestrationContextzamiast . Aby uzyskać więcej informacji na temat różnic między wersjami, zobacz artykuł Wersje rozszerzenia Durable Functions.

Uwaga

Ten mechanizm w rzeczywistości nie zatrzymuje wykonywania funkcji aktywności będącej w toku. Zamiast tego po prostu umożliwia funkcji orkiestrator ignorowanie wyniku i przechodzenie dalej. Aby uzyskać więcej informacji, zobacz dokumentację czasomierzy .

Nieobsługiwane wyjątki

Jeśli funkcja orkiestratora zakończy się niepowodzeniem z nieobsługiwanym wyjątkiem, szczegóły wyjątku są rejestrowane, a wystąpienie kończy się stanem Failed .

Uwzględnij niestandardowe właściwości wyjątków dla szczegółów błędów (.NET w trybie izolowanym)

W przypadku wykonywania przepływów pracy Durable Task w modelu izolowanym .NET błędy zadań są automatycznie serializowane w obiekcie FailureDetails. Domyślnie ten obiekt zawiera standardowe pola, takie jak:

  • ErrorType — nazwa typu wyjątku
  • Komunikat — komunikat o wyjątku
  • StackTrace — serializowany ślad stosu
  • InnerFailure — zagnieżdżony obiekt FailureDetails dla wyjątków cyklicznych wewnętrznych

Począwszy od microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0, możesz rozszerzyć to zachowanie, implementując element IExceptionPropertiesProvider (zdefiniowany w pakiecie Microsoft.DurableTask.Worker, począwszy od pakietu w wersji 1.16.1). Ten dostawca definiuje, które typy wyjątków i które z ich właściwości powinny być uwzględnione w słowniku FailureDetails.Properties.

Uwaga

  • Ta funkcja jest dostępna tylko w programie .NET Isolated . Obsługa języka Java zostanie dodana w przyszłej wersji.
  • Upewnij się, że używasz biblioteki Microsoft.Azure.Functions.Worker.Extensions.DurableTask w wersji 1.9.0 lub nowszej.
  • Upewnij się, że używasz narzędzia Microsoft.DurableTask.Worker w wersji 1.16.1 lub nowszej .

Implementowanie dostawcy właściwości wyjątku

Zaimplementuj niestandardowy element IExceptionPropertiesProvider, aby wyodrębnić i zwrócić wybrane właściwości dla określonych wyjątków. Zwrócony słownik zostanie zserializowany w polu Właściwości elementu FailureDetails, gdy zostanie zgłoszony pasujący typ wyjątku.

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
        };
    }
}

Rejestrowanie dostawcy

Zarejestruj niestandardowy element IExceptionPropertiesProvider na hoście procesu roboczego izolowanego platformy .NET, zazwyczaj w 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();

Po zarejestrowaniu każdy wyjątek zgodny z jednym z obsługiwanych typów automatycznie uwzględni skonfigurowane właściwości w parametrze FailureDetails.

Przykładowe dane wyjściowe SzczegółyBłędu

Gdy wystąpi wyjątek zgodny z konfiguracją dostawcy, orkiestracja otrzymuje serializowaną strukturę FailureDetails w następujący sposób:

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

Następne kroki