Back to "使用 Polly 来回调"

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 来回调

Sunday, 15 September 2024

一. 导言 导言 导言 导言 导言 导言 一,导言 导言 导言 导言 导言 导言

波利 这是任何.NET开发商工具箱的关键部分。 这是一个图书馆, 允许您在应用程序中定义处理例外和重试的政策 。 在本条中,我们将探讨如何: 一一 使用 Polly 处理此应用程序的重试 。

波利

虽然Polly的重试效果非常好 但并不是它所能做的, 它确实是一个工具工具 用来增加你的应用程序的抗御力。 两者都向外部服务单位和内部打电话。

这些照片取自 Polly 主页 和 Polly 中您可以使用的主要模式 :

  • 重试: 如果发生故障, 再试一次 。 当问题只是暂时的,可能会消失时,这样做是有用的。
  • 电路断路器:停止尝试 如果某事被打破或忙碌。 这样做可以避免浪费时间,使情况更糟,从而对你们有利。 该系统还可以支持系统恢复。
  • 超时:如果事情耗时太长,就放弃。 通过腾出空间和资源,这可以改善你的业绩。
  • 限速率:限制您提出或接受多少请求。 这将帮助您控制负荷,防止出现问题或处罚。
  • 后退:如果出了差错,做点别的事。 这可以改善你的用户经验,使程序保持工作状态。 在同一时间做不止一件事, 并且采取最快的一件事 。 这可以让你的程序更快 反应更灵敏

我如何使用Polly

在这一申请中,我在多个地方使用Polly。

背景翻译服务

为了启动我的翻译服务,并检查方便NMT服务器,可以使用。 Thisi 允许我在开始“提供”我的应用程序中的翻译服务之前, 检查是否有可用的服务 。 你会记得这个都用在 我的编辑器“ 玩具” 使您能够翻译标记和我的"在苍蝇上" 博客后传翻译引擎.. 因此,我必须检查一下“简易NMT”还没有下降(并能够等到它上台,这可能需要几秒钟)。

 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政策, 它将在每次重试之间等待5秒后重试3次。 如果在重试后服务仍然不可用,我们将登录一个错误并通过设置 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

我使用我的Ummi.Net图书馆的Polly处理向Ummi API提出请求时的重试。 这是图书馆的一个关键部分,因为它允许我处理与API的任何问题,并在必要时重试请求。

在这里,我设置了 HttpClient 使用 Retry 政策;在此情况下,我正在检查 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);
    }

使用重试策略 HttpClient 要求是提高可靠性的重要途径。 虽然我们喜欢认为我们的网络服务总是可以上网, 但总有一些故障时间(例如,在我的例子中, 观察塔 检测到正在等待更新, 并重新启动 Umami 容器 ) 。 因此,重新研究政策可以帮助确保您的申请能够优雅地处理这些情况。

文件系统监视器

我对Polly的另一个用法是 处理我申请中的装货和保存文件 我用一个 FileSystemWatcher 以监测存储我的标记文件的目录 。 当文件创建或更新时, 我将载入文件并处理它 。 如果当事件触发时文件仍然被写入, 这可能是个问题 。 @ info: whatsthis 所以我用 RetryPolicy 来处理这种情况。

在这里,你可以看到,我处理 IOException 键,当文件在使用时被丢弃,并重试操作。 我用一个 WaitAndRetryAsync 策略在每次重试的间隔间延迟5次重试操作。 这使我能够处理档案仍然被写到手的情况,并重试操作,直至成功。

关键是,我吐"吐"到" 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);
               ...
            });
...
    }

这也是我代码与外部服务互动的一个例子,这里是文件系统。 我预计会发生某些错误类型。 我还用这些日志 血清追踪 发送他们到Seq, 当错误者登录时, 然后寄给我电子邮件给我, 这样我就可以找出可能发生的任何问题。

同样的,一般的做法是在可以的地方处理例外, 在不能时记录这些例外, 并确保你有办法知道发生了什么。 这有助于确保您的应用程序具有弹性,并能够处理可能出现的任何问题。

电子邮件服务

我的电子邮件服务处用的是两个 CircuitBreaker 和重试策略。 重试政策用于处理电子邮件服务不可用和断路器用于处理电子邮件服务繁忙或超载的案件。

两者在电子邮件方面都很重要;SMTP是一个相对缓慢且可能不可靠的协议。

这里我处理 SmtpExplications, 当 SMTP 服务出错时, 它会先重试三次, 每次重试之间会延迟 。 如果在重试后服务仍然无法提供(以及新发送的另外两个),断路器将打开并停止发送电子邮件一分钟。 这有助于防止电子邮件服务超载(我的账户被封锁), 并增加电子邮件成功发送的机会。

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

然后在我的电子邮件发送环中使用此功能, 它等待新信件添加到频道, 然后尝试发送它们 。

这使用包装保单中的所有真知灼见性来增加电子邮件发送过程的复原力。

 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, 您可以在应用程序中处理重试、 断路器、 超时、 利率限制、 后退和套期保值 。 这有助于确保您的申请是可靠的,并能够处理可能出现的任何问题。 这篇文章里,我只谈了Polly的一个方面;重试,这是可以提高你申请的复原力和可靠性的机制。 通过使用Polly, 您可以以一致的方式处理重试, 并确保您的应用程序能够处理任何可能出现的问题 。

logo

©2024 Scott Galloway