【问题标题】:Throttle access to resource限制对资源的访问
【发布时间】:2013-08-28 06:07:46
【问题描述】:

我想知道如何为需要限制的 REST 服务实现包装器。该服务的费率仅限于例如“每分钟最多 60 个呼叫”或类似名称。过度使用资源意味着我的客户端被阻塞了很长时间,所以我必须注意不要让这种情况发生,因此我希望包装器本身受到速率限制。

编辑:删除最终想法并将其作为答案发布

【问题讨论】:

  • 嗯,“每分钟最多 60 次调用”显然意味着您需要能够说出您在最后一分钟(至少)发出的请求数量并检查它。因此,只需存储您提出请求的DateTime 列表即可。然后,在每个新请求中,检查您在最后一分钟提出了多少,并丢弃任何旧信息,因为您不再需要它了。
  • 我认为您不应该只是等待,我发现@Alex 提出的解决方案更好,您绝对应该计算在最后一分钟完成的请求。单线程管理其他线程有什么难看的?
  • @Alex 是的,存储请求列表可以让我从“请求之间的最小间隔”更改为更好的“每个时间跨度的最大请求”。上一期的请求太多了怎么办?等一下?另外我担心最近请求的收集可能是争用的来源(对于大量请求和/或长时间段。如果请求限制为 100k/小时,则需要进行大量计数(其他线程将同时锁定)。
  • @AndersForsgren 然后您可以按分钟(或其他适当的时间量)对请求进行分组。您将拥有大约 60 个元组 <minite, requests count> 的列表,并且在可以处理新请求时增加当前分钟的请求计数。基本上在重负载下,您将分分钟处理请求,但您在请求列表上要做的处理更少。至于当请求填满时该怎么办 - 您可以让线程在同步块中争夺空闲位置,或者建立一个队列,或者如果您必须等待太长时间则抛出异常。
  • @Alex 我已经更新了我的解决方案,加入了保持请求时间,因为它比前面的示例更好地解决了问题。我会接受您的答案(如果您想发布答案),否则我会将示例 sn-p 移至答案并接受我自己的答案。谢谢!

标签: c# multithreading throttling


【解决方案1】:

回答我自己的问题,受@Alex 评论的启发,这个解决了时间跨度 T 内最大 N 个请求的问题。它将最后 N 个请求的时间保存在一个列表(循环缓冲区)中并允许该请求如果最早的调用早于 T。

public class Throttler<T>
{
   private readonly long[] callTimes;
   private int cur;      
   private readonly Func<T> func;
   private readonly TimeSpan interval;
   private readonly object padlock = new object();    

   public Throttler(Func<T> func, int maxCalls, TimeSpan interval)
   {
      this.func = func;
      callTimes = new long[maxCalls];
      this.interval = interval;
      cur = 0;
   }

   public T Call()
   {
      lock (padlock)
      {
         do
         {
            long oldestCall = callTimes[(cur + 1)%callTimes.Length];
            long now = DateTime.UtcNow.Ticks;               
            if (now - oldestCall > interval.Ticks)
            {                 
               cur = (cur + 1) % callTimes.Length;                  
               callTimes[cur] = now;
               return func();
            }
            int sleepTime = (int)((interval.Ticks - (now - oldestCall))/TimeSpan.TicksPerMillisecond) + 1;               
            Thread.Sleep(sleepTime);               
         } while (true);
      }
   }
}

【讨论】:

  • 如果将func的实际调用移到同步块之外(break退出循环而不是返回并将return func()移到@的右括号之后,则可以加快请求速度987654325@) :)
猜你喜欢
  • 2011-10-14
  • 2011-05-03
  • 2018-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 2015-01-28
相关资源
最近更新 更多