首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >调整代码以使用内置RateLimit策略

调整代码以使用内置RateLimit策略
EN

Stack Overflow用户
提问于 2022-05-27 12:52:25
回答 1查看 284关注 0票数 1

我希望实现与内置RateLimit政策 (即记录器消息)中的策略相同的行为,并读取重试后的标题,并等待等待所需的准确秒数,但使用内置的RateLimit策略。

尝试

代码语言:javascript
复制
// 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));
}

作品

代码语言:javascript
复制
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;
            });
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-05-30 07:27:39

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

1)如何将记录器注入到策略中?

为此,您需要使用波莉语境

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

代码语言:javascript
复制
var context = new Context().WithLogger(logger);

然后通过Execute/ExecuteAsync调用传递

代码语言:javascript
复制
await policy.ExecuteAsync(ctx => FooAsync(), context);

最后,可以在任何用户委托(如onRetry/onRetryAsync)中使用上下文来检索传递的对象。

代码语言:javascript
复制
(exception, timeSpan, retryCount, context) =>
{
  var logger = context.GetLogger();
  logger?.LogWarning(...);
  ...
}

WithLoggerGetLogger扩展方法

代码语言:javascript
复制
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)上述限速器的工作方式是否与重试相同?

不是的。速率限制器是一种积极主动的策略,可以有效地防止资源滥用。这意味着如果超过了预定义的限制,它将抛出一个RateLimitRejectedException

当我们谈论恢复策略时,我们指的是双方之间为克服瞬态故障而预先定义的协议。因此,速率限制器是这个故事的服务器端,而重试(反应策略)是客户端。

如果您想在速率限制器中设置RetryAfter头,那么您可以这样做

代码语言:javascript
复制
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;
        });

然后,在retry的sleepDurationProvider委托内的客户端,如果responseDelegateResult<HttpResponseMessage>,则可以像这样检索该值

代码语言:javascript
复制
response.Result.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(0)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72405752

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档