Back to "Polly für Retries verwenden"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET C# Polly

Polly für Retries verwenden

Sunday, 15 September 2024

Einleitung

Polly. - (EN) Nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein. ist ein kritischer Bestandteil des Toolkits eines.NET-Entwicklers. Es ist eine Bibliothek, mit der Sie Richtlinien für die Handhabung von Ausnahmen und Wiederholungen in Ihrer Anwendung definieren können. In diesem Artikel werden wir untersuchen, wie man I. ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNG DER ENTWICKLUNGEN verwenden Sie Polly, um Retries in dieser Anwendung zu handhaben.

Polly. - (EN) Nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein, nein.

Während Polly retries wirklich gut ist es nicht alles, was es tun kann, ist es wirklich ein Toolkit für die Erhöhung der Widerstandsfähigkeit zu Ihren Anwendungen. Beide machen Anrufe an externe Dienste und intern.

Diese werden dem Polly Hauptseite und sind die wichtigsten Muster, die Sie mit Polly verwenden können:

  • Versuchen Sie es noch einmal, wenn etwas fehlschlägt. Dies kann nützlich sein, wenn das Problem vorübergehend ist und verschwinden könnte.
  • Circuit Breaker: Hör auf zu versuchen, wenn etwas kaputt oder beschäftigt ist. Das kann dir von Nutzen sein, indem du dir vermeidest, Zeit zu verschwenden und die Dinge noch schlimmer zu machen. Es kann auch das System unterstützen, um sich zu erholen.
  • Timeout: Geben Sie auf, wenn etwas zu lange dauert. Dies kann Ihre Leistung verbessern, indem Sie Platz und Ressourcen freigeben.
  • Rate Limiter: Begrenzen Sie, wie viele Anfragen Sie machen oder akzeptieren. Dadurch können Sie die Last kontrollieren und Probleme oder Strafen vermeiden.
  • Fallback: Tun Sie etwas anderes, wenn etwas fehlschlägt. Dies kann Ihre Benutzererfahrung verbessern und das Programm am Laufen halten. Hedging: Machen Sie mehr als eine Sache gleichzeitig und nehmen Sie die schnellste. Dies kann Ihr Programm schneller und reaktionsschneller machen.

Wie ich Polly verwende

In dieser Anwendung verwende ich Polly an mehreren Stellen.

HintergrundTranslateService

Für den Start meines Übersetzungsdienstes und die Überprüfung der EasyNMT-Server stehen zur Verfügung. Mit Thsi kann ich prüfen, ob der Dienst verfügbar ist, bevor ich den Übersetzungsdienst in meiner App 'biete'. Sie werden sich erinnern, dass dies für beide verwendet wird Mein Editor 'Spielzeug' damit du Markdown und für mein "on the fly" übersetzen kannst Blog-Post-Übersetzungs-Engine......................................................................................................... Es ist also entscheidend, dass EasyNMT nicht nach unten gegangen ist (und das Warten ermöglicht, bis es hochkommt; was ein paar Sekunden dauern kann).

 private async Task StartupHealthCheck(CancellationToken cancellationToken)
    {
        var retryPolicy = Policy
            .HandleResult<bool>(result => !result) // Retry when Ping returns false (service not available)
            .WaitAndRetryAsync(3, // Retry 3 times
                attempt => TimeSpan.FromSeconds(5), // Wait 5 seconds between retries
                (result, timeSpan, retryCount, context) =>
                {
                    logger.LogWarning("Translation service is not available, retrying attempt {RetryCount}", retryCount);
                });

        try
        {
            var isUp = await retryPolicy.ExecuteAsync(async () =>
            {
                return await Ping(cancellationToken); // Ping to check if the service is up
            });

            if (isUp)
            {
                logger.LogInformation("Translation service is available");
                TranslationServiceUp = true;
            }
            else
            {
                logger.LogError("Translation service is not available after retries");
                await HandleTranslationServiceFailure();
                TranslationServiceUp = false;
            }
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "An error occurred while checking the translation service availability");
            await HandleTranslationServiceFailure();
            TranslationServiceUp = false;
        }
    }

Hier können Sie sehen, dass wir eine Polly-Retry-Politik einrichten, die 3 mal mit einem 5-Sekunden-Warteraum zwischen jedem Retry wiederholen wird. Wenn der Dienst nach den Wiederholungen immer noch nicht verfügbar ist, protokollieren wir einen Fehler und behandeln den Fehler, indem wir die TranslationServiceUp Flagge zu falsch. Dadurch können alle Dienste, die den Übersetzungsdienst nutzen, wissen, dass er nicht verfügbar ist.

graph LR A[Start Health Check] --> B[Define Retry Policy] B --> C[Retry Policy: Retry 3 times] C --> D[Wait 5 seconds between retries] D --> E[Ping Translation Service] E --> F{Ping successful?} F -- Yes --> G[Log: Translation service is available] G --> H[Set TranslationServiceUp = true] F -- No --> I[Log: Translation service not available] I --> J[Check retry count] J -- Retry Limit Reached --> K[Log: Translation service not available after retries] K --> L[HandleTranslationServiceFailure] L --> M[Set TranslationServiceUp = false] J -- Retry Again --> E E --> N{Exception Occurs?} N -- Yes --> O[Log: Error occurred] O --> L

Umami.Net

Ich benutze Polly auch in meiner Umami.Net Bibliothek, um Retries zu bearbeiten, wenn ich Anfragen an die Umami API stelle. Dies ist ein kritischer Teil der Bibliothek, da es mir erlaubt, alle Probleme mit der API zu behandeln und die Anforderung zu wiederholen, wenn nötig.

Hier richte ich meine HttpClient eine Retry Policy zu verwenden; in diesem Fall überprüfe ich eine HttpStatusCode.ServiceUnavailable und versuchen, die Anfrage, wenn sie auftritt. Ich verwende auch eine Decorrelated Jitter Backoff Strategie, zwischen Retries zu warten. Dies ist eine gute Strategie zu verwenden, da es hilft, das 'Unterherden-Problem' zu vermeiden, wo alle Kunden zur gleichen Zeit wiederholen (auch wenn es nur ich :)). Dies kann dazu beitragen, die Belastung auf dem Server zu reduzieren und die Chancen der Anfrage erfolgreich zu verbessern.

     var httpClientBuilder = services.AddHttpClient<AuthService>(options =>
            {
                options.BaseAddress = new Uri(umamiSettings.UmamiPath);
            })
            .SetHandlerLifetime(TimeSpan.FromMinutes(5)) 
            .AddPolicyHandler(RetryPolicyExtension.GetRetryPolicy());


    public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 3);
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == HttpStatusCode.ServiceUnavailable)
            .WaitAndRetryAsync(delay);
    }

Verwendung einer Retry-Politik für HttpClient Anfragen sind ein wichtiger Weg, um die Zuverlässigkeit zu verbessern. Während wir gerne denken, dass unsere Web-Dienste immer verfügbar sind gibt es immer SOME Ausfallzeiten (in meinem Fall, wenn zum Beispiel Der Wachtturm erkennt, dass ein Update aussteht und startet den Umami-Container neu). Eine Wiederholungsrichtlinie kann also dazu beitragen, sicherzustellen, dass Ihre Anwendung diese Situationen anmutig handhaben kann.

FileSystemWatcher

Ein weiterer Nutzen, den ich von Polly mache, ist, wenn es um das Laden und Speichern von Dateien in meiner Anwendung geht. Ich benutze eine FileSystemWatcher um das Verzeichnis zu überwachen, in dem meine Markdown-Dateien gespeichert sind. Wenn eine Datei erstellt oder aktualisiert wird, lade ich die Datei und verarbeite sie. Dies kann ein Problem sein, wenn die Datei noch geschrieben wird, wenn das Ereignis ausgelöst wird. Also benutze ich eine RetryPolicy um diese Situation zu bewältigen.

Hier sehen Sie, ich handle mit der IOException die geworfen wird, wenn die Datei in Gebrauch ist und die Operation erneut versuchen. Ich benutze eine WaitAndRetryAsync Politik, die Operation 5 Mal mit einer Verzögerung zwischen jedem Wiederholungsversuch erneut zu versuchen. Dies ermöglicht es mir, die Situation, in der die Datei noch geschrieben wird, zu behandeln und die Operation erneut zu versuchen, bis sie erfolgreich ist.

Was hier kritisch ist, ist, dass ich 'aufwerfe' IOException von meinem SavePost Methode, die es der Polly-Politik ermöglicht, die Wiederholung zu handhaben. Dies ist ein gutes Muster zu folgen, wie es Ihnen erlaubt, die Wiederholungslogik an einem zentralen Ort zu handhaben und nicht müssen sich darum sorgen, in jeder Methode, die eine Operation erneut versuchen müssen.

Im Allgemeinen immer mit Ausnahmen umgehen, wo Sie können und sich übergeben auf eine höhere Ebene, wo Sie sie zentralisieren (oder protokollieren) können. Dies kann dazu beitragen, die Komplexität Ihres Codes zu reduzieren und es einfacher zu machen, mit Ausnahmen konsistent umzugehen.

      private async Task OnChangedAsync(WaitForChangedResult e)
    {
       ...
        var retryPolicy = Policy
            .Handle<IOException>() // Only handle IO exceptions (like file in use)
            .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromMilliseconds(500 * retryAttempt),
                (exception, timeSpan, retryCount, context) =>
                {
                    activity?.Activity?.SetTag("Retry Attempt", retryCount);
                    // Log the retry attempt
                    logger.LogWarning("File is in use, retrying attempt {RetryCount} after {TimeSpan}", retryCount,
                        timeSpan);
                });

      ...

            // Use the Polly retry policy for executing the operation
            await retryPolicy.ExecuteAsync(async () =>
            {
              ...
                var blogService = scope.ServiceProvider.GetRequiredService<IBlogService>();
                await blogService.SavePost(blogModel);
               ...
            });
...
    }

Auch dies ist ein Beispiel für meinen Code, der mit einem externen Dienst, in diesem Fall dem Dateisystem, interagiert. Wo ich bestimmte Fehlertypen verspüre, um auftreten zu können. Ich logge diese auch mit SerilogTracing welches sie an Seq sendet, welches mir eine E-Mail sendet, wenn der Error protokolliert ist, damit ich alle Probleme identifizieren kann, die auftreten könnten.

Auch hier ist der allgemeine Ansatz, mit Ausnahmen umzugehen, wo Sie können, sie zu protokollieren, wenn Sie nicht können und sicherzustellen, dass Sie einen Weg haben, zu wissen, was passiert. Dies kann dazu beitragen, sicherzustellen, dass Ihre Anwendung ist widerstandsfähig und kann alle Probleme, die auftreten können.

E-Mail-Dienst

In meinem E-Mail-Service verwende ich beide CircuitBreaker Eine Politik der Wiederholung und eine Politik der Wiederholung. Die Retry-Richtlinie wird verwendet, um den Fall zu behandeln, in dem der E-Mail-Dienst nicht verfügbar ist und der Leistungsschalter verwendet wird, um den Fall zu behandeln, in dem der E-Mail-Dienst beschäftigt oder überlastet ist.

Beides ist im Falle von E-Mails wichtig; SMTP ist ein relativ langsames und potenziell unzuverlässiges Protokoll.

Hier handle ich SmtpExceptions, wo, wenn ein Fehler vom SMTP-Dienst kommt, es zuerst dreimal mit einer Verzögerung zwischen jedem Wiederholungsversuch wiederholen wird. Wenn der Service nach den Retries immer noch nicht verfügbar ist (und zwei weitere für einen neuen Send), öffnet sich der Leistungsschalter und hört für eine Minute auf, E-Mails zu senden. Dies kann dazu beitragen, zu verhindern, dass der E-Mail-Dienst überlastet wird (und mein Konto blockiert wird) und verbessern die Chancen, dass die E-Mail erfolgreich gesendet wird.

         // Initialize the retry policy
            var retryPolicy = Policy
                .Handle<SmtpException>() // Retry on any exception
                .WaitAndRetryAsync(3, // Retry 3 times
                    attempt => TimeSpan.FromSeconds(2 * attempt),
                    (exception, timeSpan, retryCount, context) =>
                    {
                        logger.LogWarning(exception, "Retry {RetryCount} for sending email failed", retryCount);
                    });

            // Initialize the circuit breaker policy
            var circuitBreakerPolicy = Policy
                .Handle<SmtpException>()
                .CircuitBreakerAsync(
                    5,
                    TimeSpan.FromMinutes(1), 
                    onBreak: (exception, timespan) =>
                    {
                        logger.LogError("Circuit broken due to too many failures. Breaking for {BreakDuration}", timespan);
                    },
                    onReset: () =>
                    {
                        logger.LogInformation("Circuit reset. Resuming email delivery.");
                    },
                    onHalfOpen: () =>
                    {
                        logger.LogInformation("Circuit in half-open state. Testing connection...");
                    });
            _policyWrap = Policy.WrapAsync(retryPolicy, circuitBreakerPolicy);
            
            

Dies wird dann in meiner E-Mail-Sendschleife verwendet, die darauf wartet, dass neue Nachrichten dem Kanal hinzugefügt werden, dann versucht, sie zu senden.

Dies nutzt alle Funktionen in der gewickelten Richtlinie, um die Widerstandsfähigkeit des E-Mail-Sendens zu erhöhen.

 while (await _mailMessages.Reader.WaitToReadAsync(token))
        {
            BaseEmailModel? message = null;
            try
            {
                message = await _mailMessages.Reader.ReadAsync(token);

                // Execute retry policy and circuit breaker around the email sending logic
                await _policyWrap.ExecuteAsync(async () =>
                {
                    switch (message)
                    {
                        case ContactEmailModel contactEmailModel:
                            await _emailService.SendContactEmail(contactEmailModel);
                            break;
                        case CommentEmailModel commentEmailModel:
                            await _emailService.SendCommentEmail(commentEmailModel);
                            break;
                    }
                });

                _logger.LogInformation("Email from {SenderEmail} sent", message.SenderEmail);
            }
            catch (OperationCanceledException)
            {
                break;
            }
            catch (Exception exc)
            {
                _logger.LogError(exc, "Couldn't send an e-mail from {SenderEmail}", message?.SenderEmail);
            }
        }

Schlussfolgerung

Polly ist eine leistungsstarke Bibliothek, die Ihnen helfen kann, Ihre Anwendungen noch widerstandsfähiger zu machen. Durch die Verwendung von Polly können Sie Retries, Leistungsschalter, Timeouts, Rate Limiter, Fallbacks und Hedging in Ihrer Anwendung handhaben. Dies kann helfen, sicherzustellen, dass Ihre Anwendung zuverlässig ist und kann alle Probleme, die auftreten können. In diesem Beitrag habe ich wirklich nur einen Aspekt von Polly abgedeckt; Retries, sind dies ein Mechanismus, der die Widerstandsfähigkeit und Zuverlässigkeit Ihrer Anwendung verbessern kann. Durch die Verwendung von Polly können Sie Retries in einer konsistenten Weise handhaben und sicherstellen, dass Ihre Anwendung alle Probleme behandeln kann, die auftreten könnten.

logo

©2024 Scott Galloway