我有以下的异步code,它会从这么多的地方在我的项目名为:
I have the following async code that gets called from so many places in my project:
public async Task<HttpResponseMessage> MakeRequestAsync(HttpRequestMessage request)
{
var client = new HttpClient();
return await client.SendAsync(request).ConfigureAwait(false);
}
如何上面的方法被调用的一个例子:
An example of how the above method gets called:
var tasks = items.Select(async i =>
{
var response = await MakeRequestAsync(i.Url);
//do something with response
});
该ZenDesk的API,我打允许每分钟之后,我得到一个429错误200的请求。我需要做某种一个视频下载的,如果我遇到了429错误,但与异步/计谋,有可能在并行线程这么多要求等待的过程中,我不知道我怎么可以让所有的人的睡眠为5秒左右,然后恢复试。
The ZenDesk API that I'm hitting allows about 200 requests per minute after which I'm getting a 429 error. I need to do some sort of a Thread.sleep if I encounter the 429 error, but with with async/await, there may be so many requests in parallel threads waiting to process, I am not sure how I can make all of them sleep for 5 seconds or so and then resume again.
什么是解决这个问题的正确方法?我想听听快速的解决方案,以及良好的设计解决方案。
What's the correct way to approach this problem? I'd like to hear quick solutions as well as good-design solutions.
我不认为这是一个重复,因为最近标记。其他的SO海报不需要基于时间的滑动窗口(或基于时间的节流)和答案有不覆盖这种情况。当你想设置的传出请求硬限制才起作用。
I do not think that this is a duplicate, as marked recently. The other SO poster does not need a time-based sliding window (or time based throttling) and the answer there does not cover this situation. That works only when you want to set a hard limit on outgoing requests.
总之,准快速的解决办法是让在 MakeRequestAsync
法的限制。事情是这样的:
Anyway, a quasi-quick solution is to make the throttling in the MakeRequestAsync
method. Something like this:
public async Task<HttpResponseMessage> MakeRequestAsync(HttpRequestMessage request)
{
//Wait while the limit has been reached.
while(!_throttlingHelper.RequestAllowed)
{
await Task.Delay(1000);
}
var client = new HttpClient();
_throttlingHelper.StartRequest();
var result = await client.SendAsync(request).ConfigureAwait(false);
_throttlingHelper.EndRequest();
return result;
}
类 ThrottlingHelper
只是我现在做,所以你可能需要将其调试位(读 - 可能不起作用开箱即用)。
它试图是一个时间戳滑动窗口。
The class ThrottlingHelper
is just something I made now so you may need to debug it a bit (read - may not work out of the box).
It tries to be a timestamp sliding window.
public class ThrottlingHelper : IDisposable
{
//Holds time stamps for all started requests
private readonly List<long> _requestsTx;
private readonly ReaderWriterLockSlim _lock;
private readonly int _maxLimit;
private TimeSpan _interval;
public ThrottlingHelper(int maxLimit, TimeSpan interval)
{
_requestsTx = new List<long>();
_maxLimit = maxLimit;
_interval = interval;
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
public bool RequestAllowed
{
get
{
_lock.EnterReadLock();
try
{
var nowTx = DateTime.Now.Ticks;
return _requestsTx.Count(tx => nowTx - tx < _interval.Ticks) < _maxLimit;
}
finally
{
_lock.ExitReadLock();
}
}
}
public void StartRequest()
{
_lock.EnterWriteLock();
try
{
_requestsTx.Add(DateTime.Now.Ticks);
}
finally
{
_lock.ExitWriteLock();
}
}
public void EndRequest()
{
_lock.EnterWriteLock();
try
{
var nowTx = DateTime.Now.Ticks;
_requestsTx.RemoveAll(tx => nowTx - tx >= _interval.Ticks);
}
finally
{
_lock.ExitWriteLock();
}
}
public void Dispose()
{
_lock.Dispose();
}
}
您会用它作为使请求的类的成员,并创建实例是这样的:
You would use it as a member in the class that makes the requests, and instantiate it like this:
_throttlingHelper = new ThrottlingHelper(200, TimeSpan.FromMinutes(1));
不要忘记处理它,当你用它做。
Don't forget to dispose it when you're done with it.
文件有关的位 ThrottlingHelper
:
RequestAllowed
让你知道,如果你能够做到与当前的限制设置的请求。
方法 StartRequest
&放大器; EndRequest
注册/注销使用当前日期/时间的请求。
Constructor params are the maximum requests you want to be able to do in a certain interval and the interval itself as a time span. So, 200 and 1 minute means that that you want no more than 200 requests/minute.
Property RequestAllowed
lets you know if you are able to do a request with the current throttling settings.
Methods StartRequest
& EndRequest
register/unregister a request by using the current date/time.
修改/陷阱
如由@PhilipABarnes, EndRequest
可能会删除仍在进行中的请求。据我所看到的,这可以在两种情况下发生:
As indicated by @PhilipABarnes, EndRequest
can potentially remove requests that are still in progress. As far as I can see, this can happen in two situations:
提出的解决方案涉及到实际的匹配 EndRequest
由GUID或类似的方式调用 StartRequest
通话。
The proposed solution involves actually matching EndRequest
calls to StartRequest
calls by means of a GUID or something similar.