限制HttpClient请求速率的简单方法 [英] Simple way to rate limit HttpClient requests
问题描述
我正在使用System.Net.Http中的HTTPClient来针对API发出请求.该API每秒最多只能请求10个请求.
I am using the HTTPClient in System.Net.Http to make requests against an API. The API is limited to 10 requests per second.
我的代码大致如下:
List<Task> tasks = new List<Task>();
items..Select(i => tasks.Add(ProcessItem(i));
try
{
await Task.WhenAll(taskList.ToArray());
}
catch (Exception ex)
{
}
ProcessItem方法可做一些事情,但始终使用以下方法调用API:
await SendRequestAsync(..blah)
.看起来像这样:
The ProcessItem method does a few things but always calls the API using the following:
await SendRequestAsync(..blah)
. Which looks like:
private async Task<Response> SendRequestAsync(HttpRequestMessage request, CancellationToken token)
{
token.ThrowIfCancellationRequested();
var response = await HttpClient
.SendAsync(request: request, cancellationToken: token).ConfigureAwait(continueOnCapturedContext: false);
token.ThrowIfCancellationRequested();
return await Response.BuildResponse(response);
}
本来代码可以正常工作,但是当我开始使用Task.WhenAll时,我开始从API获取超出速率限制"消息.如何限制提出请求的速度?
Originally the code worked fine but when I started using Task.WhenAll I started getting 'Rate Limit Exceeded' messages from the API. How can I limit the rate at which requests are made?
值得注意的是,根据项目的不同,ProcessItem可以进行1-4个API调用.
Its worth noting that ProcessItem can make between 1-4 API calls depending on the item.
推荐答案
API每秒最多只能请求10个请求.
The API is limited to 10 requests per second.
然后让您的代码执行10个批次的请求,确保它们至少花费一秒钟:
Then just have your code do a batch of 10 requests, ensuring they take at least one second:
Items[] items = ...;
int index = 0;
while (index < items.Length)
{
var timer = Task.Delay(TimeSpan.FromSeconds(1.2)); // ".2" to make sure
var tasks = items.Skip(index).Take(10).Select(i => ProcessItemsAsync(i));
var tasksAndTimer = tasks.Concat(new[] { timer });
await Task.WhenAll(tasksAndTimer);
index += 10;
}
更新
我的ProcessItems方法根据项目进行1-4个API调用.
My ProcessItems method makes 1-4 API calls depending on the item.
在这种情况下,批处理不是合适的解决方案.您需要将异步方法限制为某个特定的 number ,即SemaphoreSlim
.棘手的部分是您想允许随着时间的流逝.
In this case, batching is not an appropriate solution. You need to limit an asynchronous method to a certain number, which implies a SemaphoreSlim
. The tricky part is that you want to allow more calls over time.
我还没有尝试过这段代码,但是我要遵循的总体思路是具有一个周期性函数,该函数可以将信号量最多释放 10次.所以,像这样:
I haven't tried this code, but the general idea I would go with is to have a periodic function that releases the semaphore up to 10 times. So, something like this:
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(10);
private async Task<Response> ThrottledSendRequestAsync(HttpRequestMessage request, CancellationToken token)
{
await _semaphore.WaitAsync(token);
return await SendRequestAsync(request, token);
}
private async Task PeriodicallyReleaseAsync(Task stop)
{
while (true)
{
var timer = Task.Delay(TimeSpan.FromSeconds(1.2));
if (await Task.WhenAny(timer, stop) == stop)
return;
// Release the semaphore at most 10 times.
for (int i = 0; i != 10; ++i)
{
try
{
_semaphore.Release();
}
catch (SemaphoreFullException)
{
break;
}
}
}
}
用法:
// Start the periodic task, with a signal that we can use to stop it.
var stop = new TaskCompletionSource<object>();
var periodicTask = PeriodicallyReleaseAsync(stop.Task);
// Wait for all item processing.
await Task.WhenAll(taskList);
// Stop the periodic task.
stop.SetResult(null);
await periodicTask;
这篇关于限制HttpClient请求速率的简单方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!