简体   繁体   English

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

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

It has to be trivial, but I just cannot get through it. 它必须是琐碎的,但我只是无法解决。 I have to limit amount of tasks (let's say connections, emails sent or clicks in the button) per amount of time. 我必须限制每个时间的任务量(比如连接,发送的电子邮件或单击按钮)。 So eg I can send 1000 emails per hour. 因此,例如,我每小时可以发送1000封电子邮件。

How can I do that in c#? 我该如何在C#中做到这一点? I don't know and don't care how much time each operation will take. 我不知道,也不在乎每个操作要花费多少时间。 I just want to make sure that for last hour, only 1000 will be executed. 我只想确保最后一个小时只能执行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);
    }
 }

Assuming a rolling hour window: 假设滚动小时窗:

Maintain a list of when actions were done. 维护何时执行操作的列表。

Each time you want to do your action, remove all in the list not within the hour. 每次您要执行操作时,都不要在一个小时内删除列表中的所有内容。

If there are fewer than 1000 then do the action and add a record to your list. 如果少于1000,则执行操作并将一条记录添加到列表中。


Assuming hourly: 假设每小时:

Create a proxy method and a variable that is incremented for every action, and reduced to zero on the hour. 创建一个代理方法和一个变量,该变量将为每个操作递增,并在每小时减少为零。

Do your action if the counter is < 1000. 如果计数器<1000,请执行操作。

The above solution looked fine. 上述解决方案看起来不错。 Here is my trimmed down version: 这是我的精简版本:

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 >> ),但是我将通过添加适当的代理对象来手动实现缓冲。

You may also consider storing {action, time, user} information in a database and get number of actions in a last hour fomr the DB (or similar persisted storager) if you need to handle Application pool restarts / crashes. 如果需要处理应用程序池重启/崩溃,您还可以考虑在数据库中存储{action,time,user}信息,并在数据库(或类似的持久性存储器)的最后一小时内获取操作数。 Otherwise clever user may circumvent your in-memory protection with overloading your server. 否则,聪明的用户可能会因服务器超载而绕过内存保护。

You can create a persistent counter for every user. 您可以为每个用户创建一个持久计数器。 Every time you receive a request (for sending an email) you need to check the value of the counter and the date of the counter creation. 每次收到请求(发送电子邮件)时,都需要检查计数器的值和计数器的创建日期。

  • If the count is greater than the limit you refuse the request 如果计数大于限制,则您拒绝请求
  • If the date is older than an hour you reset the counter and set the new creation date 如果日期早于一个小时,则重置计数器并设置新的创建日期
  • If the date is correct and the count is under the limit you increase the counter 如果日期正确且计数在限制范围内,则增加计数器

Only in the last two cases the request is executed. 仅在最后两种情况下才执行请求。

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

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