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



我想用内置的RateLimit策略实现与下面的策略相同的行为,即记录器消息,并读取Retry-After报头并等待所需的确切秒数,但使用内置的RateLimit策略。

尝试
// 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;
});
}

有很多问题,让我一一回答。

1)如何将logger注入到Policy中?

你需要使用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(...);
...
}

WithLoggerGetLogger扩展方法

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头,那么你可以这样做

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委托的客户端,你可以检索这样的值,如果response是一个DelegateResult<HttpResponseMessage>

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

最新更新