调整代码以使用内置比率策略

发布于 2025-02-01 21:59:32 字数 2001 浏览 3 评论 0原文

我想实现与下面的策略相同的行为,并以内置的比塔级策略,即记录器消息并读取重试标头并等待需要等待但使用内置比率策略的确切秒数。

尝试

// TODO: No logger message and not sure if it waits the time taken from the Retry-After header.
public static AsyncRateLimitPolicy Limit<T>(ILogger<T> logger)
{
    return Policy.RateLimitAsync(RateLimitRetryCount, TimeSpan.FromSeconds(5));
}

工作

public static AsyncRetryPolicy<RestResponse> AsyncRateLimit<T>(ILogger<T> logger)
{
    return Policy.HandleResult<RestResponse>(response => response.StatusCode == HttpStatusCode.TooManyRequests)
        .WaitAndRetryAsync(RateLimitRetryCount,
            (attemptCount, restResponse, _) =>
            {
                var retryAfterHeader = restResponse?.Result?.Headers?.SingleOrDefault(h => h.Name == "Retry-After");
                double secondsInterval = 0;

                if (retryAfterHeader != null)
                {
                    var value = retryAfterHeader.Value?.ToString();
                    if (!double.TryParse(value, out secondsInterval))
                    {
                        secondsInterval = Math.Pow(2, attemptCount);
                    }
                }

                return TimeSpan.FromSeconds(secondsInterval);
            },
            (response, timeSpan, retryCount, _) =>
            {
                logger.LogTrace(
                    "The API request has been rate limited. HttpStatusCode={StatusCode}. Waiting {Seconds} seconds before retry. Number attempt {RetryCount}. Uri={Url}; RequestResponse={Content}",
                    response.Result.StatusCode, timeSpan.TotalSeconds, retryCount, response.Result.ResponseUri, response.Result.Content);

                return Task.CompletedTask;
            });
}

I want to achieve the same behavior as the policy below with the built-in RateLimit policy, i.e. the logger message and to read the Retry-After header and wait the exact seconds that were needed to wait for but using the built-in RateLimit policy.

Attempt

// TODO: No logger message and not sure if it waits the time taken from the Retry-After header.
public static AsyncRateLimitPolicy Limit<T>(ILogger<T> logger)
{
    return Policy.RateLimitAsync(RateLimitRetryCount, TimeSpan.FromSeconds(5));
}

Works

public static AsyncRetryPolicy<RestResponse> AsyncRateLimit<T>(ILogger<T> logger)
{
    return Policy.HandleResult<RestResponse>(response => response.StatusCode == HttpStatusCode.TooManyRequests)
        .WaitAndRetryAsync(RateLimitRetryCount,
            (attemptCount, restResponse, _) =>
            {
                var retryAfterHeader = restResponse?.Result?.Headers?.SingleOrDefault(h => h.Name == "Retry-After");
                double secondsInterval = 0;

                if (retryAfterHeader != null)
                {
                    var value = retryAfterHeader.Value?.ToString();
                    if (!double.TryParse(value, out secondsInterval))
                    {
                        secondsInterval = Math.Pow(2, attemptCount);
                    }
                }

                return TimeSpan.FromSeconds(secondsInterval);
            },
            (response, timeSpan, retryCount, _) =>
            {
                logger.LogTrace(
                    "The API request has been rate limited. HttpStatusCode={StatusCode}. Waiting {Seconds} seconds before retry. Number attempt {RetryCount}. Uri={Url}; RequestResponse={Content}",
                    response.Result.StatusCode, timeSpan.TotalSeconds, retryCount, response.Result.ResponseUri, response.Result.Content);

                return Task.CompletedTask;
            });
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挽梦忆笙歌 2025-02-08 21:59:32

有多个问题,所以让我回答所有问题。

1)如何将记录仪注入策略?

您需要使用 polly的上下文

上下文是在策略之外创建的。它被用作存储任何任意信息的容器

var context = new Context().WithLogger(logger);

,然后通过execute/executeasync调用

await policy.ExecuteAsync(ctx => FooAsync(), context);

最终您可以在任何用户委托中使用上下文(例如> >) onretry/onretryAsync)要检索传递的对象

(exception, timeSpan, retryCount, context) =>
{
  var logger = context.GetLogger();
  logger?.LogWarning(...);
  ...
}

withlogger and getlogger扩展方法

public static class ContextExtensions
{
    private static readonly string LoggerKey = "LoggerKey";

    public static Context WithLogger(this Context context, ILogger logger)
    {
        context[LoggerKey] = logger;
        return context;
    }

    public static ILogger GetLogger(this Context context)
    {
        if (context.TryGetValue(LoggerKey, out object logger))
        {
            return logger as ILogger;
        }
        return null;
    }
}

2)在上述费率限制器工作中与重试的方式相同吗?

否。费率限制器是A 主动策略,可用于防止资源滥用。这意味着如果超过预定义的限制,它将抛出ratelimitRejectedException

每当我们谈论弹性策略时,我们都会提到双方之间的预定义协议,以克服瞬态失败。因此,速率限制器是此故事的服务器端,而重试(反应性策略)是客户端。

如果要在速率限制器中设置retryafter标题,那么您可以这样做,

IAsyncPolicy<HttpResponseMessage> limit = Policy
    .RateLimitAsync(RateLimitRetryCount, TimeSpan.FromSeconds(5), RateLimitRetryCount,
        (retryAfter, context) => {
            var response = new HttpResponseMessage(System.Net.HttpStatusCode.TooManyRequests);
            response.Headers.Add("Retry-After", retryAfter.TotalSeconds.ToString());
            return response;
        });

然后在重试的sleepdurationProvider委派中的客户端上,您可以像如果响应delegateresult&lt; httpresponsemessage&gt;

response.Result.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(0)

There were multiple questions so let me answer all of them.

1) How to inject logger to a Policy?

You need to use Polly's context for that.

The context is created outside of the policy. It is used as a container to store any arbitrary information

var context = new Context().WithLogger(logger);

Then it is passed through the Execute/ExecuteAsync call

await policy.ExecuteAsync(ctx => FooAsync(), context);

Finally you can use the context in any user delegate (like onRetry/onRetryAsync) to retrieve the passed object

(exception, timeSpan, retryCount, context) =>
{
  var logger = context.GetLogger();
  logger?.LogWarning(...);
  ...
}

The WithLogger and GetLogger extension methods

public static class ContextExtensions
{
    private static readonly string LoggerKey = "LoggerKey";

    public static Context WithLogger(this Context context, ILogger logger)
    {
        context[LoggerKey] = logger;
        return context;
    }

    public static ILogger GetLogger(this Context context)
    {
        if (context.TryGetValue(LoggerKey, out object logger))
        {
            return logger as ILogger;
        }
        return null;
    }
}

2) Does the above rate limiter work in the same way as the retry?

No. The rate limiter is a proactive policy which can be useful to prevent resource abuse. That means it will throw an RateLimitRejectedException if the predefined limit is exceeded.

Whenever we are talking about resilience strategy we are referring to a predefined protocol between the two parties to overcome on transient failures. So the rate limiter is the server-side of this story whereas the retry (reactive policy) is the client-side.

If you want to set the RetryAfter header in your rate limiter then you can do that like this

IAsyncPolicy<HttpResponseMessage> limit = Policy
    .RateLimitAsync(RateLimitRetryCount, TimeSpan.FromSeconds(5), RateLimitRetryCount,
        (retryAfter, context) => {
            var response = new HttpResponseMessage(System.Net.HttpStatusCode.TooManyRequests);
            response.Headers.Add("Retry-After", retryAfter.TotalSeconds.ToString());
            return response;
        });

Then on the client-side inside your retry's sleepDurationProvider delegate you can retrieve that value like this if the response is a DelegateResult<HttpResponseMessage>

response.Result.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(0)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文