How to limit method usage per amount of time?

前端 未结 6 1508
南旧
南旧 2020-12-31 08:12

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

相关标签:
6条回答
  • 2020-12-31 08:35

    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. Otherwise clever user may circumvent your in-memory protection with overloading your server.

    0 讨论(0)
  • 2020-12-31 08:37

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-31 08:41

    You can use Rx extensions (How to use the new BufferWithTimeOrCount in Rx that returns IObservable<IObservable<T>> instead of IObservable<IList<T>>), but I would implement the buffering manually by adding an appropriate proxy object.

    0 讨论(0)
  • 2020-12-31 08:43

    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.

    0 讨论(0)
  • 2020-12-31 08:48
     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);
        }
     }
    
    0 讨论(0)
  • 2020-12-31 08:49

    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.


    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.

    0 讨论(0)
提交回复
热议问题