当超出速率限制时如何重试 Polly 速率限制?

som*_*men 3 c# rate-limiting polly retry-logic

我有以下策略设置:

// Slack api for postMessage is generally 1 per channel per second.
// Some workspace specific limits may apply.
// We just limit everything to 1 second.
var slackApiRateLimitPerChannelPerSecond = 1;

var rateLimit = Policy.RateLimitAsync(slackApiRateLimitPerChannelPerSecond, TimeSpan.FromSeconds(slackApiRateLimitPerChannelPerSecond),
    (retryAfter, _) => retryAfter.Add(TimeSpan.FromSeconds(slackApiRateLimitPerChannelPerSecond)));
Run Code Online (Sandbox Code Playgroud)

这应该:

  • 速率限制请求直到 1 req/s
  • 速率受限时重试

我无法将其包装成第二个会重试的策略......

我可以像这样重试:

try
{
   _policy.Execute(...)
}
catch(RateLimitedException ex)
{
   // Policy.Retry with ex.RetryAfter
}

Run Code Online (Sandbox Code Playgroud)

但这似乎不对。

我想重试几次(3?)次,以便该方法更具弹性 - 我该怎么做?

Pet*_*ala 5

我可能会迟到,但让我投入 2 美分。

速率限制器

该策略的引擎以无锁方式实现令牌桶算法。这有一个含义,所以它并不像你直觉想象的那样工作。

例如,从这个策略角度来看,每秒 1 个请求与每分钟 60 个请求相同。
实际上,后者不应该强制均匀分配(但确实如此)!所以,你不能像这样使用它:

  • 前 10 秒发出 50 个请求
  • 45秒无任何请求
  • 最后 5 秒内可以再发出 9 个请求,但未达到限制

作为共享策略的速率限制器

对于 Polly,大多数保单都是无国籍的。这意味着两个执行不需要共享任何内容。

但对于断路器来说,控制器内部有一个状态。因此,您应该在多次执行中使用同一个实例。

在 Bulkhead 和 Rate Limiter 政策的情况下,状态并不那么明显。它们隐藏在实现中。但同样的规则也适用于此,您应该在多个线程之间共享相同的策略实例以实现所需的结果。

速率限制器与速率门

速率限制器本身可以在客户端和服务器端使用。服务器端可以主动拒绝过多的请求,以缓解过度泛洪。而客户端可以主动自我限制发出的请求,以遵守服务器和客户端之间的契约。

这个策略更适合服务器端(参见属性RetryAfter)。在客户端,速率门实现可能更合适,它通过利用队列和计时器来延迟传出请求。

带重试功能的速率限制器

如果重试和速率限制器都位于客户端

var retryPolicy = Policy
    .Handle<RateLimitRejectedException>()
    .WaitAndRetry(
        3,
        (int _, Exception ex, Context __) => ((RateLimitRejectedException)ex).RetryAfter,
        (_, __, ___, ____) => { });
Run Code Online (Sandbox Code Playgroud)

如果重试位于客户端而速率限制器位于服务器端

var retryPolicy = Policy<HttpResponseMessage>
    .HandleResult(res => res.StatusCode == HttpStatusCode.TooManyRequests)
    .WaitAndRetry(
        3,
        (int _, DelegateResult<HttpResponseMessage> res, Context __)
            => res.Result.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(0));
Run Code Online (Sandbox Code Playgroud)