【问题标题】:Calls-per-day rate limiting in ASP.NET MVC 3?ASP.NET MVC 3 中的每日调用率限制?
【发布时间】:2011-08-03 12:38:18
【问题描述】:

我已经看到 Jarrod Dixon 的解决方案 (Best way to implement request throttling in ASP.NET MVC?) 用于实现每秒调用速率限制。我现在正试图弄清楚如何为每天 N-calls 构建一个类似的过滤器。

我正在构建一个开发者 API,其中免费帐户每天可获取约 100 次调用,而付费帐户可获得更高的速率限制。在 MVC 3 中限制每日调用次数的最佳方法是什么?

【问题讨论】:

    标签: asp.net-mvc-3 rate-limiting


    【解决方案1】:

    我认为内存结构在这里不够用,因为您需要测量很长的持续时间。在这种情况下,IIS 回收会出现问题。因此,我建议在数据库中记录用户对资源的访问,并且只允许在过去 24 小时内访问 100 次。

    另一方面,这是我们实现的漏桶限制器(对于故障相对不重要的短期限制来说,这更方便)。使用 .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;
                }
            }
        }
    
    }
    

    【讨论】:

    • 我强烈建议不要在 lock 语句之外声明(并初始化 now)。尽管您的应用程序不太可能发生这种情况,但如果调用线程在 lock 语句上被阻塞很长时间,您可能会得到与 IsPermitted 调用不同的结果。
    猜你喜欢
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    相关资源
    最近更新 更多