如何限制每次使用的方法
本文关键字:方法 何限制 | 更新日期: 2023-09-27 18:11:01
这应该是微不足道的,但我就是无法完成它。我必须限制每段时间的任务数量(比如连接、发送邮件或点击按钮)。例如:我每小时可以发1000封邮件。
我如何在c#中做到这一点?我不知道也不关心每次操作要花多少时间。我只是想确保在最后一个小时,只执行1000个
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);
}
}
假设滚动小时窗口:
维护一个操作完成时间的列表。
每次你想做你的事情时,删除列表中所有不在一小时内的事情。
如果少于1000则执行该操作并在列表中添加一条记录。
假设每小时:
创建一个代理方法和一个变量,每个操作递增,每小时减为零。
如果计数器为<1000 .
上面的解决方案看起来不错。这是我修改后的版本:
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);
}
}
}
}
你可以使用Rx扩展(如何在Rx中使用新的BufferWithTimeOrCount返回IObservable
如果需要处理应用程序池重启/崩溃,您也可以考虑在数据库中存储{action, time, user}信息,并从DB(或类似的持久化存储器)获取最近一小时内的操作数量。否则,聪明的用户可能会绕过内存保护,使服务器过载。
您可以为每个用户创建一个持久计数器。每次收到请求(用于发送电子邮件)时,都需要检查计数器的值和创建计数器的日期。
- 如果计数大于限制,则拒绝请求
- 如果日期大于一个小时,则重置计数器并设置新的创建日期
- 如果日期正确且计数低于限制,则增加计数器
只在最后两种情况下执行请求