Как ограничить использование метода на количество времени?
Это должно быть тривиально, но я просто не могу пройти через это.
Я должен ограничить количество заданий (например, соединения, отправленные сообщения или клики на кнопке) за количество времени. Так, например, Я могу отправить 1000 писем в час.
Как я могу сделать это в С#? Я не знаю и не волнует, сколько времени займет каждая операция. Я просто хочу убедиться, что за последний час будет выполнено только 1000.
Ответы
Ответ 1
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);
}
}
Ответ 2
Предполагая окно календарного часа:
Поддерживать список действий, выполняемых.
Каждый раз, когда вы хотите сделать свое действие, удалите все в списке не в течение часа.
Если число меньше 1000, выполните действие и добавьте запись в список.
Предполагая ежечасно:
Создайте прокси-метод и переменную, которая увеличивается для каждого действия и уменьшается до нуля в течение часа.
Сделайте свое действие, если счетчик равен < 1000.
Ответ 3
Вы можете использовать расширения Rx (Как использовать новый BufferWithTimeOrCount в Rx, который возвращает IObservable < IObservable <T → > вместо IObservable < IList <T → ), но я бы выполнил буферизацию вручную, добавив соответствующий прокси-объект.
Ответ 4
Вы также можете рассмотреть возможность хранения {действия, времени, пользователя} информации в базе данных и получить количество действий за последний час fomr БД (или аналогичного сохраняемого хранилища), если вам необходимо обработать перезапуск/сбой пула приложений. В противном случае умный пользователь может обойти вашу защиту в памяти при перегрузке вашего сервера.
Ответ 5
Вы можете создать постоянный счетчик для каждого пользователя. Каждый раз, когда вы получаете запрос (для отправки электронной почты), вам нужно проверить значение счетчика и дату создания счетчика.
- Если счет больше лимита, вы отказываетесь от запроса
- Если дата старше часа, вы reset счетчик и установите новую дату создания
- Если дата правильная и счетчик находится под лимитом, вы увеличиваете счетчик
Только в двух последних случаях запрос выполняется.
Ответ 6
Вышеупомянутое решение выглядело отлично. Вот моя урезанная версия:
public class EmailRateHelper
{
private int RequestsPerInterval = 30;
private Queue<DateTime> History = new Queue<DateTime>();
private TimeSpan Interval = new TimeSpan(0, 1, 0);
public EmailRateHelper() { }
public EmailRateHelper(int RequestsPerInterval, TimeSpan Interval)
{
this.RequestsPerInterval = RequestsPerInterval;
this.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);
}
}
}