Back to "Χρήση της Πόλλυ για Απομάκρυνση"

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

Χρήση της Πόλλυ για Απομάκρυνση

Sunday, 15 September 2024

Εισαγωγή

ΠόλυCity name (optional, probably does not need a translation) είναι ένα κρίσιμο μέρος της εργαλειοθήκης κάθε προγραμματιστή.NET. Είναι μια βιβλιοθήκη που σας επιτρέπει να καθορίσετε πολιτικές για το χειρισμό των εξαιρέσεων και τις επαναλήψεις στην εφαρμογή σας. Σε αυτό το άρθρο, θα εξερευνήσουμε πώς να Ι χρησιμοποιήστε την Polly για να χειριστείτε τις επαναλήψεις σε αυτή την εφαρμογή.

ΠόλυCity name (optional, probably does not need a translation)

Αν και η Πόλι ξαναπατάει πολύ καλά δεν είναι το μόνο που μπορεί να κάνει, είναι πραγματικά μια εργαλειοθήκη για την προσθήκη ανθεκτικότητας στις εφαρμογές σας. Και οι δύο κάνουν κλήσεις σε εξωτερικές υπηρεσίες και εσωτερικά.

Αυτές λαμβάνονται από το Κύρια σελίδα της Polly και είναι τα κύρια πρότυπα που μπορείτε να χρησιμοποιήσετε με την Polly:

  • Ξαναπροσπάθησε αν κάτι αποτύχει. Αυτό μπορεί να είναι χρήσιμο όταν το πρόβλημα είναι προσωρινό και μπορεί να εξαφανιστεί.
  • Circuit Breaker: Σταμάτα να προσπαθείς αν κάτι είναι σπασμένο ή απασχολημένο. Αυτό μπορεί να σας ωφελήσει αποφεύγοντας το χάσιμο χρόνου και κάνοντας τα πράγματα χειρότερα. Μπορεί επίσης να υποστηρίξει το σύστημα για να ανακάμψει.
  • Τάιμ άουτ: Παράτα τα αν κάτι αργήσει πολύ. Αυτό μπορεί να βελτιώσει την απόδοσή σας με την απελευθέρωση του χώρου και των πόρων.
  • Βαθμολογήστε Limiter: Περιορίστε πόσα αιτήματα κάνετε ή αποδέχεστε. Αυτό μπορεί να σας δώσει τη δυνατότητα να ελέγξετε το φορτίο και να αποτρέψετε προβλήματα ή κυρώσεις.
  • Κάνε κάτι άλλο αν κάτι αποτύχει. Αυτό μπορεί να βελτιώσει την εμπειρία του χρήστη σας και να κρατήσει το πρόγραμμα σε λειτουργία. Hedging: Κάντε περισσότερα από ένα πράγματα ταυτόχρονα και πάρτε το γρηγορότερο. Αυτό μπορεί να κάνει το πρόγραμμά σας πιο γρήγορο και πιο ανταποκρίνεται.

Πώς Χρησιμοποιώ την Πόλι

Σε αυτή την εφαρμογή χρησιμοποιώ την Polly σε πολλά μέρη.

Background TranslateService

Για την έναρξη της μεταφραστικής μου υπηρεσίας και τον έλεγχο των διακομιστών EasyNMT είναι διαθέσιμα. Thsi μου επιτρέπει να ελέγξετε την υπηρεσία είναι διαθέσιμη πριν από την έναρξη της "προσφοράς" της μεταφραστικής υπηρεσίας στην εφαρμογή μου. Θα θυμάστε ότι αυτό είναι και τα δύο που χρησιμοποιούνται για Ο αρχισυντάκτης μου 'παιχνίδι' για να σας δώσει τη δυνατότητα να μεταφράσετε Markdown και για μου 'on the fly' blog post translation engine. Οπότε είναι κρίσιμο να ελέγξω ότι το EasyNMT δεν έχει πέσει (και να ενεργοποιήσω την αναμονή μέχρι να ανέβει, πράγμα που μπορεί να πάρει μερικά δευτερόλεπτα).

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

Εδώ μπορείτε να δείτε έχουμε δημιουργήσει μια πολιτική Polly Retry που θα ξαναδοκιμάσει 3 φορές με 5 δευτερόλεπτα αναμονή μεταξύ κάθε retry. Εάν η υπηρεσία εξακολουθεί να μην είναι διαθέσιμη μετά τις επαναλήψεις, καταγράφουμε ένα σφάλμα και χειριζόμαστε την αποτυχία με τη ρύθμιση του TranslationServiceUp Σημαία προς ψεύτικη. Αυτό επιτρέπει σε κάθε υπηρεσία που χρησιμοποιεί την υπηρεσία μετάφρασης να γνωρίζει ότι δεν είναι διαθέσιμη.

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

Επίσης χρησιμοποιώ την Polly στη βιβλιοθήκη Umami.Net για να χειρίζομαι τις επαναλήψεις όταν υποβάλλω αιτήματα στο API Umami. Αυτό είναι ένα κρίσιμο μέρος της βιβλιοθήκης, καθώς μου επιτρέπει να χειριστώ οποιαδήποτε θέματα με το API και να ξαναδοκιμάσω το αίτημα εάν είναι απαραίτητο.

Εδώ έφτιαξα το δικό μου. HttpClient σε αυτή την περίπτωση είμαι έλεγχος για ένα HttpStatusCode.ServiceUnavailable και να ξαναδοκιμάσω το αίτημα αν συμβεί. Χρησιμοποιώ επίσης ένα... Decorrelated Jitter Backoff στρατηγική για να περιμένουμε ανάμεσα στις επαναλήψεις. Αυτή είναι μια καλή στρατηγική για να χρησιμοποιήσετε, καθώς βοηθά στην αποφυγή του προβλήματος "καταδύσεις αγέλη" όπου όλοι οι πελάτες ξαναπροσπαθούν ταυτόχρονα (ακόμα και αν είναι μόνο εγώ:)). Αυτό μπορεί να βοηθήσει στη μείωση του φορτίου στο διακομιστή και στη βελτίωση των πιθανοτήτων του αιτήματος που επιτυγχάνεται.

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

Χρησιμοpiοίηση ια piολιτική εpiαναpiροσpiάθεια HttpClient Τα αιτήματα είναι ένας σημαντικός τρόπος βελτίωσης της αξιοπιστίας. Αν και μας αρέσει να πιστεύουμε ότι οι υπηρεσίες μας είναι πάντα διαθέσιμες υπάρχει πάντα ΜΕΡΙΚΑ downtime (στην περίπτωσή μου όταν για παράδειγμα Σκοπιά Ανιχνεύει μια ενημέρωση εκκρεμεί και επανεκκινεί το δοχείο Umami). Έτσι, έχοντας μια πολιτική επαναπροσπάθειας σε εφαρμογή μπορεί να βοηθήσει να διασφαλιστεί ότι η εφαρμογή σας μπορεί να χειριστεί αυτές τις καταστάσεις με χάρη.

ΑρχείοSystemWatcherName

Μια άλλη χρήση που κάνω από την Πόλι είναι όταν ασχολούνται με τη φόρτωση και την αποθήκευση αρχείων στην αίτησή μου. Χρησιμοποιώ ένα... FileSystemWatcher να παρακολουθώ τον κατάλογο όπου αποθηκεύονται τα αρχεία μου. Όταν ένα αρχείο δημιουργείται ή ενημερώνεται Γεμίζω το αρχείο και το επεξεργάζομαι. Αυτό μπορεί να είναι ένα πρόβλημα αν το αρχείο εξακολουθεί να γράφεται όταν το γεγονός ενεργοποιείται. Οπότε χρησιμοποιώ ένα... RetryPolicy για να χειριστεί αυτή την κατάσταση.

Εδώ μπορείτε να δείτε ότι χειρίζομαι το IOException που ρίχνεται όταν το αρχείο είναι σε χρήση και ξαναδοκιμάστε την επιχείρηση. Χρησιμοποιώ ένα... WaitAndRetryAsync πολιτική για να ξαναδοκιμάσετε την επιχείρηση 5 φορές με μια καθυστέρηση μεταξύ κάθε επαναπροσπάθειας. Αυτό μου επιτρέπει να χειριστώ την κατάσταση όπου το αρχείο εξακολουθεί να γράφεται και να ξαναδοκιμάσω την επιχείρηση μέχρι να πετύχει.

Αυτό που είναι κρίσιμο εδώ είναι ότι ρίχνω το "up" στο IOException από το δικό μου SavePost μέθοδος που επιτρέπει στην πολιτική Polly να χειριστεί την επανάληψη. Αυτό είναι ένα καλό μοτίβο για να ακολουθήσετε, καθώς σας επιτρέπει να χειριστείτε τη λογική ξαναδοκιμάστε σε ένα κεντρικό μέρος και δεν χρειάζεται να ανησυχείτε για αυτό σε κάθε μέθοδο που μπορεί να χρειαστεί να ξαναδοκιμάσετε μια επιχείρηση.

Γενικά πάντα να χειριστεί τις εξαιρέσεις όπου μπορείτε και εμετός σε υψηλότερο επίπεδο όπου μπορείτε να τα χειριστείτε με πιο κεντρικό τρόπο (ή να τα καταγράψετε). Αυτό μπορεί να βοηθήσει στη μείωση της πολυπλοκότητας του κώδικα σας και να καταστήσει ευκολότερη τη διαχείριση των εξαιρέσεων με συνεπή τρόπο.

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

Και πάλι αυτό είναι ένα παράδειγμα του κώδικα μου που αλληλεπιδρά με μια εξωτερική υπηρεσία, σε αυτή την περίπτωση το σύστημα αρχείων. Όπου περιμένω να συμβούν συγκεκριμένοι τύποι λάθους. Επίσης καταγράφω αυτά με τη χρήση SerilogTracing Το οποίο τα στέλνει στον Seq, το οποίο στη συνέχεια μου στέλνει ένα email όταν το σβήσιμο είναι συνδεδεμένο, έτσι ώστε να μπορώ να αναγνωρίσω οποιαδήποτε ζητήματα που μπορεί να προκύψουν.

Και πάλι, η γενική προσέγγιση είναι να χειριστείτε τις εξαιρέσεις όπου μπορείτε, να τις καταγράψετε όταν δεν μπορείτε και να διασφαλίσετε ότι έχετε έναν τρόπο να ξέρετε τι συμβαίνει. Αυτό μπορεί να βοηθήσει να διασφαλιστεί ότι η εφαρμογή σας είναι ανθεκτική και μπορεί να χειριστεί τυχόν ζητήματα που μπορεί να προκύψουν.

Υπηρεσία ηλεκτρονικού ταχυδρομείου

Στην υπηρεσία ηλεκτρονικού ταχυδρομείου μου χρησιμοποιώ και τα δύο CircuitBreaker μοτίβο και μια πολιτική επαναπροσπάθειας. Η πολιτική επαναπροσπάθειας χρησιμοποιείται για να χειριστεί την περίπτωση όπου η υπηρεσία ηλεκτρονικού ταχυδρομείου δεν είναι διαθέσιμη και ο διακόπτης κυκλώματος χρησιμοποιείται για να χειριστεί την περίπτωση όπου η υπηρεσία ηλεκτρονικού ταχυδρομείου είναι απασχολημένος ή υπερφορτωμένος.

Και τα δύο αυτά είναι σημαντικά στην περίπτωση των μηνυμάτων ηλεκτρονικού ταχυδρομείου; SMTP είναι ένα σχετικά αργό και δυνητικά αναξιόπιστο πρωτόκολλο.

Εδώ χειρίζομαι Smtp Εκτός από, όπου όταν ένα σφάλμα προέρχεται από την υπηρεσία SMTP θα ξαναδοκιμάσει τρεις φορές με μια καθυστέρηση μεταξύ κάθε επαναπροσπάθεια. Εάν η υπηρεσία δεν είναι ακόμα διαθέσιμη μετά τις επαναλήψεις (και δύο ακόμη για μια νέα αποστολή), ο διακόπτης κυκλώματος θα ανοίξει και θα σταματήσει να στέλνει μηνύματα ηλεκτρονικού ταχυδρομείου για ένα λεπτό. Αυτό μπορεί να βοηθήσει να αποφευχθεί η υπερφόρτωση της υπηρεσίας ηλεκτρονικού ταχυδρομείου (και ο λογαριασμός μου είναι μπλοκαρισμένος) και να βελτιωθούν οι πιθανότητες να σταλεί το email με επιτυχία.

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

Αυτό χρησιμοποιείται στη συνέχεια στο Email μου Αποστολή βρόχο που περιμένει νέα μηνύματα να προστεθούν στο κανάλι στη συνέχεια προσπαθεί να τα στείλει.

Αυτό χρησιμοποιεί όλη την functioanlity στην τυλιγμένη πολιτική για να προσθέσει ανθεκτικότητα στη διαδικασία αποστολής ηλεκτρονικού ταχυδρομείου.

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

Συμπέρασμα

Polly είναι μια ισχυρή βιβλιοθήκη που μπορεί να σας βοηθήσει να προσθέσετε ανθεκτικότητα στις εφαρμογές σας. Με τη χρήση Polly μπορείτε να χειριστείτε retraries, διακόπτες κυκλώματος, timeouts, περιορισμούς ταχύτητας, fallbacks και αντιστάθμιση στην εφαρμογή σας. Αυτό μπορεί να βοηθήσει να διασφαλιστεί ότι η εφαρμογή σας είναι αξιόπιστη και μπορεί να χειριστεί τυχόν ζητήματα που μπορεί να προκύψουν. Σε αυτή τη θέση πραγματικά μόλις κάλυψε μια πτυχή της Polly? retrays, αυτά είναι ένας μηχανισμός που μπορεί να βελτιώσει την ανθεκτικότητα και την αξιοπιστία της εφαρμογής σας. Με τη χρήση της Polly μπορείτε να χειριστείτε τις επαναλήψεις με συνεπή τρόπο και να διασφαλίσετε ότι η εφαρμογή σας μπορεί να χειριστεί τυχόν ζητήματα που μπορεί να προκύψουν.

logo

©2024 Scott Galloway