Ограничение количества вызовов в день в ASP.NET MVC 3? - PullRequest
3 голосов
/ 12 апреля 2011

Я видел решение Джаррода Диксона ( Лучший способ реализовать регулирование запросов в ASP.NET MVC? ) для реализации ограничения количества вызовов в секунду. Сейчас я пытаюсь выяснить, как создать аналогичный фильтр для N-вызовов в день.

Я создаю API-интерфейс для разработчиков, в котором бесплатные аккаунты получают ~ 100 звонков в день, а платные аккаунты получают более высокий лимит ставок. Каков наилучший способ ограничения ежедневных звонков в MVC 3?

1 Ответ

3 голосов
/ 12 апреля 2011

Я не думаю, что здесь достаточно структуры в памяти из-за большой продолжительности, которую вам нужно измерить. В этом случае переработка IIS будет проблематичной. Поэтому я бы рекомендовал записать доступ пользователей к ресурсу в БД и разрешить подсчет только 100 за последние 24 часа.

С другой стороны, вот наша реализация ограничителя утечек (который более удобен для краткосрочного ограничения, когда отказ относительно не важен). Использование параллельных коллекций .NET 4 может улучшить несколько грубую блокировку в этой реализации:

public class RateLimiter
{
    private readonly double numItems;
    private readonly double ratePerSecond;
    private readonly Dictionary<object, RateInfo> rateTable = 
        new Dictionary<object, RateInfo>();
    private readonly object rateTableLock = new object();
    private readonly double timePeriod;

    public RateLimiter(double numItems, double timePeriod)
    {
        this.timePeriod = timePeriod;
        this.numItems = numItems;
        ratePerSecond = numItems / timePeriod;
    }

    public double Count
    {
        get
        {
            return numItems;
        }
    }

    public double Per
    {
        get
        {
            return timePeriod;
        }
    }

    public bool IsPermitted(object key)
    {
        RateInfo rateInfo;
        var permitted = true;
        var now = DateTime.UtcNow;
        lock (rateTableLock)
        {
            var expiredKeys = 
                rateTable
                .Where(kvp => 
                    (now - kvp.Value.LastCheckTime) 
                    > TimeSpan.FromSeconds(timePeriod))
                .Select(k => k.Key)
                .ToArray();
            foreach (var expiredKey in expiredKeys)
            {
                rateTable.Remove(expiredKey);
            }
            var dataExists = rateTable.TryGetValue(key,
                                                   out rateInfo);
            if (dataExists)
            {
                var timePassedSeconds = (now - rateInfo.LastCheckTime).TotalSeconds;
                var newAllowance = 
                     Math.Min(
                         rateInfo.Allowance 
                         + timePassedSeconds 
                         * ratePerSecond,
                         numItems);
                if (newAllowance < 1d)
                {
                    permitted = false;
                }
                else
                {
                    newAllowance -= 1d;
                }
                rateTable[key] = new RateInfo(now,
                                              newAllowance);
            }
            else
            {
                rateTable.Add(key,
                              new RateInfo(now,
                                           numItems - 1d));
            }

        }
        return permitted;
    }

    public void Reset(object key)
    {
        lock (rateTableLock)
        {
            rateTable.Remove(key);
        }
    }

    private struct RateInfo
    {
        private readonly double allowance;
        private readonly DateTime lastCheckTime;

        public RateInfo(DateTime lastCheckTime, double allowance)
        {
            this.lastCheckTime = lastCheckTime;
            this.allowance = allowance;
        }

        public DateTime LastCheckTime
        {
            get
            {
                return lastCheckTime;
            }
        }

        public double Allowance
        {
            get
            {
                return allowance;
            }
        }
    }

}
...