Polly费率限制过早
我正在努力围绕Polly Ration-Limit政策。
public class RateLimiter
{
private readonly AsyncRateLimitPolicy _throttlingPolicy;
private readonly Action<string> _rateLimitedAction;
public RateLimiter(int numberOfExecutions, TimeSpan perTimeSpan, Action<string> rateLimitedAction)
{
_throttlingPolicy = Policy.RateLimitAsync(numberOfExecutions, perTimeSpan);
_rateLimitedAction = rateLimitedAction;
}
public async Task<T> Throttle<T>(Func<Task<T>> func)
{
var result = await _throttlingPolicy.ExecuteAndCaptureAsync(func);
if (result.Outcome == OutcomeType.Failure)
{
var retryAfter = (result.FinalException as RateLimitRejectedException)?.RetryAfter ?? TimeSpan.FromSeconds(1);
_rateLimitedAction($"Rate limited. Should retry in {retryAfter}.");
return default;
}
return result.Result;
}
}
在我的控制台应用程序中,我正在实例化比率
,每10秒最多5个呼叫。
var rateLimiter = new RateLimiter(5, TimeSpan.FromSeconds(10), err => Console.WriteLine(err));
var rdm = new Random();
while (true)
{
var result = await rateLimiter.Throttle(() => Task.FromResult(rdm.Next(1, 10)));
if (result != default) Console.WriteLine($"Result: {result}");
await Task.Delay(200);
}
我希望看到5个结果,并且在第六个结果中受到限制。但这就是我所得到的,
Result: 9
Rate limited. Should retry in 00:00:01.7744615.
Rate limited. Should retry in 00:00:01.5119933.
Rate limited. Should retry in 00:00:01.2313921.
Rate limited. Should retry in 00:00:00.9797322.
Rate limited. Should retry in 00:00:00.7309150.
Rate limited. Should retry in 00:00:00.4812646.
Rate limited. Should retry in 00:00:00.2313643.
Result: 7
Rate limited. Should retry in 00:00:01.7982864.
Rate limited. Should retry in 00:00:01.5327321.
Rate limited. Should retry in 00:00:01.2517093.
Rate limited. Should retry in 00:00:00.9843077.
Rate limited. Should retry in 00:00:00.7203371.
Rate limited. Should retry in 00:00:00.4700262.
Rate limited. Should retry in 00:00:00.2205184.
我也尝试使用 executeasync
而不是 executeandcaptureasync
,并且没有更改结果。
public async Task<T> Throttle<T>(Func<Task<T>> func)
{
try
{
var result = await _throttlingPolicy.ExecuteAsync(func);
return result;
}
catch (RateLimitRejectedException ex)
{
_rateLimitedAction($"Rate limited. Should retry in {ex.RetryAfter}.");
return default;
}
}
这对我没有任何意义。我缺少什么吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
速率限制器的工作方式与您预期的方式不同。
假设我有500个请求,我想将其限制为每分钟50。
期望:在前50个执行后,如果限制限制者的执行时间不到一分钟,则将启动。
这种直观的方法没有考虑到传入负载的平等分布。这可能会引起以下可观察的行为:
polly的费率限制器使用漏水库算法这可以通过以下方式工作:
从
上面描述中最重要的信息是:漏水的桶算法使用恒定速率空为空水桶。更新14/11/22
让我自己纠正。 Polly的费率限制器使用令牌 bucket而不是泄漏桶。还有其他算法,例如固定窗口计数器,滑动窗口日志或滑动窗口计数器。您可以阅读有关替代方案在这里内部系统设计访谈第1卷第4章的第4章
因此,让我们谈论令牌桶algorithm:
(来源)
如果我们仔细审查实现,那么我们可以看到以下内容:
ratelimiterEngine
的静态方法irateLimiter上调用方法
接口ratelimiterFactory
揭示了一种创建lockfreetokenbucketratelimiter
的方法>和
bucketcapacity
)!如果您对实际实施感兴趣,则可以找到在这里。 (几乎每行评论)
我想强调更多一件事。速率限制器确实 执行任何重试。如果您想在罚款时间结束后继续执行,那么您必须自己执行。通过编写一些自定义代码或将重试策略与速率限制策略相结合。
The rate limiter works in a bit different way than you might expect.
Let's suppose I have 500 requests and I want to throttle it to 50 per minute.
Expectation: After the first 50 executions the rate limiter kicks in if they were executed less than a minute.
This intuitive approach does not put into account the equal distribution of the incoming load. This might induce the following observable behaviour:
Polly's rate limiter uses the Leaky bucket algorithmThis works in the following way:
So, technically speaking:
The most important information from the above description is the following: the leaky bucket algorithm uses a constant rate to empty the bucket.UPDATE 14/11/22
Let me correct myself. Polly's rate limiter is using token bucket not leaky bucket. There are also other algorithms like fixed window counter, sliding window log or sliding window counter. You can read about the alternatives here or inside the System Design Interview Volume 1 book's chapter 4
So, let's talk about the token bucket algorithm:
(Source)
If we scrutinise the implementation then we can see the following things:
RateLimiterPolicy
calls theRateLimiterEngine
's static methodRateLimiterEngine
calls a method on aIRateLimiter
interfaceRateLimiterFactory
exposes a method to createLockFreeTokenBucketRateLimiter
Please be aware of how the parameters are named (
onePer
andbucketCapacity
)!If you are interested about the actual implementation then you can find here. (Almost each line is commented)
I want to emphasize one more thing. The rate limiter does not perform any retry. If you want to continue the execution after the penalty time is over then you have to do it yourself. Either by writing some custom code or by combining a retry policy with the rate limiter policy.
有一个过载接受第三参数 -
maxburst
:默认值是
1
,如果将其设置为numberfexecutions
您会看到第一个执行的所需效果,尽管此后它将在您的情况下变速为相似的模式。观察(我想这是基于限制器如何“释放”资源和var Oneper = timespan.fromticks(pertimespan.ticks / numberOfexecutions);
计算,但是我没有太深的挖掘,但是基于文档和代码,速率限制似乎正在发生“ 1执行perpertimespan
/code>/numberfexecutions
/code>“速率而不是“
numberofexecutions
”中的任何选定的pertimespan
>”):添加定期等待几秒钟将带回不过,“爆发”。
另请参见:
There is an overload accepting third parameter -
maxBurst
:The default value is
1
, if you will set it tonumberOfExecutions
you will see the desired effect for the first execution, though after that it will deteriorate to the similar pattern as you observe (I would guess it is based on how the limiter "frees" the resources andvar onePer = TimeSpan.FromTicks(perTimeSpan.Ticks / numberOfExecutions);
calculation, but I have not dug too deep, but based on the docs and code it seems that rate limiting is happening with "1 execution perperTimeSpan
/numberOfExecutions
" rate rather than "numberOfExecutions
in any selectedperTimeSpan
"):Adding periodic wait for several seconds brings back the "bursts" though.
Also see: