2011-04-12 4 views
3

Jarrod Dixon의 솔루션 (Best way to implement request throttling in ASP.NET MVC?)이 초당 호출 속도 제한을 구현 한 것을 보았습니다. 이제는 N 통화 당 비슷한 필터를 만드는 방법을 알아 내려고 노력 중입니다.ASP.NET MVC 3에서 일일 통화량 제한은 무엇입니까?

무료 계정에 하루에 ~ 100 통화가 발생하고 유료 계정의 요금이 더 높은 개발자 API를 구축하고 있습니다. MVC 3에서 일일 통화량 제한을 수행하는 가장 좋은 방법은 무엇입니까?

답변

3

측정해야 할 오랜 기간 때문에 인 메모리 구조가 충분하지 않을 것이라고 생각합니다. IIS 재활용은이 경우 문제가 될 수 있습니다. 따라서 DB의 리소스에 대한 사용자 액세스를 기록하고 지난 24 시간 동안 만 100을 허용하는 것이 좋습니다.

한편, 여기에는 누출 버킷 리미터 (leaky bucket limiter) 구현이 있습니다 (이는 상대적으로 중요하지 않은 부분을 제한하는 단기적 제한에 더 유용합니다). .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; 
      } 
     } 
    } 

} 
+2

lock 문 외부에서 선언 (및 'now'초기화)을하지 않는 것이 좋습니다. 응용 프로그램에서 발생하기는 어렵지 만, 호출 스레드가'lock' 문에서 오랫동안 차단되면 'IsPermitted' 호출에서 예상 한 것과 다른 결과를 얻을 수 있습니다. –

관련 문제