gpt4 book ai didi

c# - 客户端请求限速

转载 作者:行者123 更新时间:2023-11-30 22:53:10 25 4
gpt4 key购买 nike

我正在为外部 API 设计 .NET 客户端应用程序。它将有两个主要职责:

  • 同步 - 向 API 发出一批请求并定期将响应保存到我的数据库。
  • 客户端 - 我的客户端用户对 API 请求的传递。

服务的文档指定了以下关于在给定时间段内可以发出的最大请求数的规则:

一天中:

  • 每小时最多 6000 个请求(每秒约 1.67 个)
  • 每分钟最多 120 个请求(每秒 2 个)
  • 每秒最多 3 个请求

晚上:

  • 每小时最多 8000 个请求(每秒约 2.23 个)
  • 每分钟最多 150 个请求(每秒 2.5 个)
  • 每秒最多 3 个请求

超过这些限制不会导致立即锁定 - 不会抛出异常。但是供应商可能会生气,联系我们然后禁止我们使用他的服务。所以我需要有一些请求延迟机制来防止这种情况发生。这是我的看法:

public async Task MyMethod(Request request)
{
await _rateLimter.WaitForNextRequest(); // awaitable Task with calculated Delay
await _api.DoAsync(request);
_rateLimiter.AppendRequestCounters();
}

最安全和最简单的选择是仅遵守最低速率限制,即每 2 秒最多 3 个请求。但由于“同步”责任,需要尽可能多地使用这些限制。

因此下一个选项是根据当前请求计数添加延迟。我试着自己做点什么,我也用过 RateLimiter by David Desmaisons , 本来可以的,但是有一个问题:

假设我的客户每天向 API 发送 3 个请求,我们将看到:

  • 每 120 个请求延迟 20 秒
  • 每 6000 个请求延迟约 15 分钟

如果我的应用程序只是关于“同步”,这是可以接受的,但“客户端”请求不能等那么久。

我在网上搜索过,也读过有关 token /漏桶和滑动窗口算法的内容,但我无法将它们转化为我的案例和 .NET,因为它们主要涉及拒绝超过限制。我找到了 this repothat repo , 但它们都只是服务端解决方案。

类似于 QoS 的速率拆分,因此“同步”速率较慢,而“客户端”速率较快,这不是一种选择。

假设将测量当前请求速率,如何计算下一个请求的延迟,使其能够适应当前情况,遵守所有最大速率并且不会超过 5 秒?接近极限时逐渐减速。

最佳答案

这可以通过使用您在 GitHub 上链接的库来实现。我们需要使用由 3 个 CountByIntervalAwaitableConstraint 组成的 TimeLimiter,如下所示:

var hourConstraint = new CountByIntervalAwaitableConstraint(6000, TimeSpan.FromHours(1));
var minuteConstraint = new CountByIntervalAwaitableConstraint(120, TimeSpan.FromMinutes(1))
var secondConstraint = new CountByIntervalAwaitableConstraint(3, TimeSpan.FromSeconds(1));

var timeLimiter = TimeLimiter.Compose(hourConstraint, minuteConstraint, secondConstraint);

我们可以通过这样做来测试看看这是否有效:

for (int i = 0; i < 1000; i++)
{
await timeLimiter;
Console.WriteLine($"Iteration {i} at {DateTime.Now:T}");
}

这将每秒运行 3 次,直到我们达到 120 次迭代(迭代 119),然后等到分钟结束并继续每秒运行 3 次。我们还可以(再次使用库)通过使用 AsDelegatingHandler() 扩展方法轻松地将 TimeLimiter 与 HTTP 客户端一起使用,如下所示:

var handler = TimeLimiter.Compose(hourConstraint, minuteConstraint, secondConstraint);
var client = new HttpClient(handler);

我们也可以使用 CancellationToken,但据我所知不能同时将其用作 HttpClient 的处理程序。以下是如何将它与 HttpClient 一起使用:

var timeLimiter = TimeLimiter.Compose(hourConstraint, minuteConstraint, secondConstraint);
var client = new HttpClient();

for (int i = 0; i < 100; i++)
{
await composed.Enqueue(async () =>
{
var client = new HttpClient();
var response = await client.GetAsync("https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty");
if (response.IsSuccessStatusCode)
Console.WriteLine(await response.Content.ReadAsStringAsync());
else
Console.WriteLine($"Error code {response.StatusCode} reason: {response.ReasonPhrase}");
}, new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token);
}

编辑以解决更多 OP 问题:

如果您想确保用户可以发送请求而不必等待限制结束,我们需要每秒/分钟/小时为我们的用户分配一定数量的请求。因此我们为此需要一个新的 TimeLimiter 并调整我们的 API TimeLimiter。这是两个新的:

var apiHourConstraint = new CountByIntervalAwaitableConstraint(5500, TimeSpan.FromHours(1));
var apiMinuteConstraint = new CountByIntervalAwaitableConstraint(100, TimeSpan.FromMinutes(1));
var apiSecondConstraint = new CountByIntervalAwaitableConstraint(2, TimeSpan.FromSeconds(1));

// TimeLimiter for calls automatically to the API
var apiTimeLimiter = TimeLimiter.Compose(apiHourConstraint, apiMinuteConstraint, apiSecondConstraint);

var userHourConstraint = new CountByIntervalAwaitableConstraint(500, TimeSpan.FromHours(1));
var userMinuteConstraint = new CountByIntervalAwaitableConstraint(20, TimeSpan.FromMinutes(1));
var userSecondConstraint = new CountByIntervalAwaitableConstraint(1, TimeSpan.FromSeconds(1));

// TimeLimiter for calls made manually by a user to the API
var userTimeLimiter = TimeLimiter.Compose(userHourConstraint, userMinuteConstraint, userSecondConstraint);

您可以根据自己的需要随意调整数字。

现在开始使用它:
我看到您正在使用中央方法来执行您的请求,这使它更容易。我将添加一个可选的 bool 参数,用于确定它是自动执行的请求还是用户发出的请求。 (如果您想要的不仅仅是自动和手动请求,您可以用枚举替换此参数)

public static async Task DoRequest(Request request, bool manual = false)
{
TimeLimiter limiter;
if (manual)
limiter = TimeLimiterManager.UserLimiter;
else
limiter = TimeLimiterManager.ApiLimiter;

await limiter;
_api.DoAsync(request);
}

static class TimeLimiterManager
{
public static TimeLimiter ApiLimiter { get; }

public static TimeLimiter UserLimiter { get; }

static TimeLimiterManager()
{
var apiHourConstraint = new CountByIntervalAwaitableConstraint(5500, TimeSpan.FromHours(1));
var apiMinuteConstraint = new CountByIntervalAwaitableConstraint(100, TimeSpan.FromMinutes(1));
var apiSecondConstraint = new CountByIntervalAwaitableConstraint(2, TimeSpan.FromSeconds(1));

// TimeLimiter to control access to the API for automatically executed requests
ApiLimiter = TimeLimiter.Compose(apiHourConstraint, apiMinuteConstraint, apiSecondConstraint);

var userHourConstraint = new CountByIntervalAwaitableConstraint(500, TimeSpan.FromHours(1));
var userMinuteConstraint = new CountByIntervalAwaitableConstraint(20, TimeSpan.FromMinutes(1));
var userSecondConstraint = new CountByIntervalAwaitableConstraint(1, TimeSpan.FromSeconds(1));

// TimeLimiter to control access to the API for manually executed requests
UserLimiter = TimeLimiter.Compose(userHourConstraint, userMinuteConstraint, userSecondConstraint);
}
}

这并不完美,因为如果用户不是每分钟执行 20 个 API 调用,但您的自动化系统需要每分钟执行 100 个以上,它就必须等待。

关于昼夜差异:您可以为 Api/UserLimiter 使用 2 个支持字段,并在 { get {...} 中返回适当的字段属性

关于c# - 客户端请求限速,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57522524/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com