它必须是微不足道的,但我无法通过它。 我必须限制任务量(让我们说连接,发送电子邮件或点击按钮)的时间量。所以例如我每小时可以发送1000封电子邮件。
我怎样才能在c#中做到这一点?我不知道也不关心每次操作需要多少时间。我只是想确保在最后一小时内只执行1000次。
答案 0 :(得分:18)
class EventLimiter
{
Queue<DateTime> requestTimes;
int maxRequests;
TimeSpan timeSpan;
public EventLimiter(int maxRequests, TimeSpan timeSpan)
{
this.maxRequests = maxRequests;
this.timeSpan = timeSpan;
requestTimes = new Queue<DateTime>(maxRequests);
}
private void SynchronizeQueue()
{
while ((requestTimes.Count > 0) && (requestTimes.Peek().Add(timeSpan) < DateTime.UtcNow))
requestTimes.Dequeue();
}
public bool CanRequestNow()
{
SynchronizeQueue();
return requestTimes.Count < maxRequests;
}
public void EnqueueRequest()
{
while (!CanRequestNow())
Thread.Sleep(requestTimes.Peek().Add(timeSpan).Subtract(DateTime.UtcNow));
// Was: System.Threading.Thread.Sleep(1000);
requestTimes.Enqueue(DateTime.UtcNow);
}
}
答案 1 :(得分:4)
假设滚动小时窗口:
维护行动何时完成的清单。
每次您想要采取行动时,请在一小时内删除列表中的所有内容。
如果少于1000,则执行操作并将记录添加到列表中。
假设每小时:
创建一个代理方法和一个为每个操作递增的变量,并在一小时内减少到零。
如果计数器是&lt; 1000。
答案 2 :(得分:2)
您可以使用Rx扩展名(How to use the new BufferWithTimeOrCount in Rx that returns IObservable<IObservable<T>> instead of IObservable<IList<T>>),但我会通过添加适当的代理对象来手动实现缓冲。
答案 3 :(得分:2)
上述解决方案看起来很好。这是我的修剪版本:
public class EmailRateHelper
{
private int _requestsPerInterval;
private Queue<DateTime> _history;
private TimeSpan _interval;
public EmailRateHelper()
: this(30, new TimeSpan(0, 1, 0)) { }
public EmailRateHelper(int requestsPerInterval, TimeSpan interval)
{
_requestsPerInterval = requestsPerInterval;
_history = new Queue<DateTime>();
_interval = interval;
}
public void SleepAsNeeded()
{
DateTime now = DateTime.Now;
_history.Enqueue(now);
if (_history.Count >= _requestsPerInterval)
{
var last = _history.Dequeue();
TimeSpan difference = now - last;
if (difference < _interval)
{
System.Threading.Thread.Sleep(_interval - difference);
}
}
}
}
答案 4 :(得分:1)
如果需要处理应用程序池重启/崩溃,您还可以考虑将{action,time,user}信息存储在数据库中并获取DB(或类似的持久存储器)的最后一小时内的操作数。否则,聪明的用户可能会因服务器过载而绕过内存保护。
答案 5 :(得分:1)
您可以为每个用户创建一个持久计数器。每次收到请求(发送电子邮件)时,您都需要检查计数器的值和创建计数器的日期。
仅在最后两种情况下执行请求。