繁体   English   中英

如何限制每次使用方法的时间?

[英]How to limit method usage per amount of time?

它必须是琐碎的,但我只是无法解决。 我必须限制每个时间的任务量(比如连接,发送的电子邮件或单击按钮)。 因此,例如,我每小时可以发送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 <T >>而不是IObservable <IList <T >> ),但是我将通过添加适当的代理对象来手动实现缓冲。

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

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

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

仅在最后两种情况下才执行请求。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM