如何限制每次使用的方法

本文关键字:方法 何限制 | 更新日期: 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>而不是IObservable>),但我将通过添加适当的代理对象来手动实现缓冲。

如果需要处理应用程序池重启/崩溃,您也可以考虑在数据库中存储{action, time, user}信息,并从DB(或类似的持久化存储器)获取最近一小时内的操作数量。否则,聪明的用户可能会绕过内存保护,使服务器过载。

您可以为每个用户创建一个持久计数器。每次收到请求(用于发送电子邮件)时,都需要检查计数器的值和创建计数器的日期。

  • 如果计数大于限制,则拒绝请求
  • 如果日期大于一个小时,则重置计数器并设置新的创建日期
  • 如果日期正确且计数低于限制,则增加计数器

只在最后两种情况下执行请求