Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 什么';什么是好的速率限制算法?_Python_Algorithm_Message Queue - Fatal编程技术网

Python 什么';什么是好的速率限制算法?

Python 什么';什么是好的速率限制算法?,python,algorithm,message-queue,Python,Algorithm,Message Queue,我可以使用一些伪代码,或者更好的Python。我正在尝试为Python IRC bot实现一个速率限制队列,它部分工作,但如果有人触发的消息少于限制(例如,速率限制为每8秒5条消息,该人仅触发4条),并且下一个触发器超过8秒(例如,16秒后),bot将发送消息,但是队列已满,机器人等待8秒,即使8秒的时间已经过去,因此不需要等待。令牌桶的实现相当简单 从一个有5个代币的桶开始 每5/8秒:如果桶中的令牌少于5个,则添加一个 每次您想要发送一条消息:如果bucket≥1个令牌,取出一个令牌并发送消

我可以使用一些伪代码,或者更好的Python。我正在尝试为Python IRC bot实现一个速率限制队列,它部分工作,但如果有人触发的消息少于限制(例如,速率限制为每8秒5条消息,该人仅触发4条),并且下一个触发器超过8秒(例如,16秒后),bot将发送消息,但是队列已满,机器人等待8秒,即使8秒的时间已经过去,因此不需要等待。

令牌桶的实现相当简单

从一个有5个代币的桶开始

每5/8秒:如果桶中的令牌少于5个,则添加一个

每次您想要发送一条消息:如果bucket≥1个令牌,取出一个令牌并发送消息。否则,请等待/删除消息/任何内容

(显然,在实际代码中,您将使用整数计数器而不是真正的令牌,并且您可以通过存储时间戳来优化每5/8秒一步)


再次阅读问题,如果每8秒完全重置一次速率限制,则有一个修改:

从一个时间戳开始,
last\u send
,在很久以前的某个时间(例如,在纪元)。同样,从相同的5令牌桶开始

按“每5/8秒一次”规则

每次发送消息时:首先,检查是否
last\u send
≥ 8秒前。如果是,则填充桶(将其设置为5个令牌)。其次,如果bucket中有令牌,则发送消息(否则,drop/wait/etc)。第三,将上次发送设置为现在

这应该适用于这种情况


实际上,我已经使用这样的策略(第一种方法)编写了一个IRC机器人。它是用Perl编写的,不是Python,但下面是一些代码来说明:

这里的第一部分处理向bucket添加令牌。您可以看到基于时间(从第二行到最后一行)添加令牌的优化,然后最后一行将bucket内容钳制到最大值(MESSAGE_BURST)

my$start\u time=time;
...
#铲斗装卸
我的$bucket=$conn->{fujiko_limit_bucket};
my$lasttx=$conn->{fujiko_limit_lasttx};
$bucket+=($start\u time-$lasttx)/消息间隔;
($bucket{fujiko_limit_bucket}=$bucket;
$conn->{fujiko_limit_lasttx}=$start_time;

如您所见,实际的桶处理代码非常小,大约四行。其余代码是优先级队列处理。bot具有优先级队列,因此,例如,与它聊天的人无法阻止它执行重要的踢/禁止任务。

一种解决方案是为每个队列项附加时间戳并丢弃该项8秒后。您可以在每次将队列添加到时执行此检查


仅当您将队列大小限制为5并在队列已满时丢弃任何添加内容时,此操作才有效。

保留最后五行的发送时间。保留排队的消息,直到最近的第五条消息(如果存在)过去至少8秒(最后五条作为时间数组):


在您排队的函数之前使用此decorator@RateLimited(ratepersec)

基本上,这会检查自上次以来是否已过了1/秒的速率,如果没有,则等待剩余的时间,否则它不会等待。这有效地限制了速率/秒。装饰器可以应用于任何想要速率限制的函数

在您的情况下,如果您希望每8秒最多发送5条消息,请在sendToQueue函数之前使用@RateLimited(0.625)

import time

def RateLimited(maxPerSecond):
    minInterval = 1.0 / float(maxPerSecond)
    def decorate(func):
        lastTimeCalled = [0.0]
        def rateLimitedFunction(*args,**kargs):
            elapsed = time.clock() - lastTimeCalled[0]
            leftToWait = minInterval - elapsed
            if leftToWait>0:
                time.sleep(leftToWait)
            ret = func(*args,**kargs)
            lastTimeCalled[0] = time.clock()
            return ret
        return rateLimitedFunction
    return decorate

@RateLimited(2)  # 2 per second at most
def PrintNumber(num):
    print num

if __name__ == "__main__":
    print "This should print 1,2,3... at about 2 per second."
    for i in range(1,100):
        PrintNumber(i)
在这里,如果您只想在消息到达得太快时丢弃消息(而不是排队,这是有意义的,因为队列可能会变得任意大):

rate=5.0;//单位:消息
per=8.0;//单位:秒
容差=速率;//单位:消息
last_check=now();//浮点,例如usec精度。单位:秒
当(收到消息时):
当前=现在();
通过的时间=当前-最后一次检查;
最后一次检查=当前;
津贴+=通过时间*(费率/每小时);
如果(津贴>费率):
余量=费率;//节流阀
如果(容差<1.0):
丢弃消息();
其他:
转发消息();
容差-=1.0;
此解决方案中没有数据结构、计时器等,它工作得很干净:)要看到这一点,“余量”最多以每秒5/8个单位的速度增长,即每8秒最多5个单位。转发的每封邮件扣除一个单位,因此每八秒钟发送的邮件不能超过五封

请注意,
rate
应该是一个整数,即没有非零小数部分,否则算法将无法正常工作(实际速率将不是
rate/per
)。例如,
rate=0.5;per=1.0不起作用,因为
余量
永远不会增长到1.0。但
rate=1.0;per=2.0工作正常。

这个怎么样:

long check_time = System.currentTimeMillis();
int msgs_sent_count = 0;

private boolean isRateLimited(int msgs_per_sec) {
    if (System.currentTimeMillis() - check_time > 1000) {
        check_time = System.currentTimeMillis();
        msgs_sent_count = 0;
    }

    if (msgs_sent_count > (msgs_per_sec - 1)) {
        return true;
    } else {
        msgs_sent_count++;
    }

    return false;
}

为了阻止处理直到消息可以发送,从而排队等待更多消息,antti的漂亮解决方案也可以修改如下:

rate = 5.0; // unit: messages
per  = 8.0; // unit: seconds
allowance = rate; // unit: messages
last_check = now(); // floating-point, e.g. usec accuracy. Unit: seconds

when (message_received):
  current = now();
  time_passed = current - last_check;
  last_check = current;
  allowance += time_passed * (rate / per);
  if (allowance > rate):
    allowance = rate; // throttle
  if (allowance < 1.0):
    time.sleep( (1-allowance) * (per/rate))
    forward_message();
    allowance = 0.0;
  else:
    forward_message();
    allowance -= 1.0;
rate=5.0;//单位:信息
per=8.0;//单位:秒
津贴=费率;//单位:信息
最后一次检查=现在();//浮点,例如usec精度。单位:秒
当(收到消息时):
当前=现在();
通过的时间=当前-最后一次检查;
最后一次检查=当前;
津贴+=通过时间*(费率/每小时);
如果(津贴>费率):
津贴=费率;//掐死
如果(容差<1.0):
睡眠时间((1-津贴)*(每小时/费率))
转发消息();
余量=0.0;
其他:
转发消息();
容差-=1.0;

它只是等待,直到有足够的余量发送消息。为了不以两倍的速率开始,允许也可以用0初始化。

如果有人仍然感兴趣,我将使用这个简单的可调用类与定时LRU键值存储结合使用,以限制每个IP的请求速率。使用deque,但可以重写为与列表一起使用

from collections import deque
import time


class RateLimiter:
    def __init__(self, maxRate=5, timeUnit=1):
        self.timeUnit = timeUnit
        self.deque = deque(maxlen=maxRate)

    def __call__(self):
        if self.deque.maxlen == len(self.deque):
            cTime = time.time()
            if cTime - self.deque[0] > self.timeUnit:
                self.deque.append(cTime)
                return False
            else:
                return True
        self.deque.append(time.time())
        return False

r = RateLimiter()
for i in range(0,100):
    time.sleep(0.1)
    print(i, "block" if r() else "pass")
我需要一个变化
rate = 5.0; // unit: messages
per  = 8.0; // unit: seconds
allowance = rate; // unit: messages
last_check = now(); // floating-point, e.g. usec accuracy. Unit: seconds

when (message_received):
  current = now();
  time_passed = current - last_check;
  last_check = current;
  allowance += time_passed * (rate / per);
  if (allowance > rate):
    allowance = rate; // throttle
  if (allowance < 1.0):
    time.sleep( (1-allowance) * (per/rate))
    forward_message();
    allowance = 0.0;
  else:
    forward_message();
    allowance -= 1.0;
from collections import deque
import time


class RateLimiter:
    def __init__(self, maxRate=5, timeUnit=1):
        self.timeUnit = timeUnit
        self.deque = deque(maxlen=maxRate)

    def __call__(self):
        if self.deque.maxlen == len(self.deque):
            cTime = time.time()
            if cTime - self.deque[0] > self.timeUnit:
                self.deque.append(cTime)
                return False
            else:
                return True
        self.deque.append(time.time())
        return False

r = RateLimiter()
for i in range(0,100):
    time.sleep(0.1)
    print(i, "block" if r() else "pass")
case class Limiter[-A, +B](callsPerSecond: (Double, Double), f: A ⇒ B) extends (A ⇒ B) {

  import Thread.sleep
  private def now = System.currentTimeMillis / 1000.0
  private val (calls, sec) = callsPerSecond
  private var allowance  = 1.0
  private var last = now

  def apply(a: A): B = {
    synchronized {
      val t = now
      val delta_t = t - last
      last = t
      allowance += delta_t * (calls / sec)
      if (allowance > calls)
        allowance = calls
      if (allowance < 1d) {
        sleep(((1 - allowance) * (sec / calls) * 1000d).toLong)
      }
      allowance -= 1
    }
    f(a)
  }

}
val f = Limiter((5d, 8d), { 
  _: Unit ⇒ 
    println(System.currentTimeMillis) 
})
while(true){f(())}
import time

class Object(object):
    pass

def get_throttler(rate, per):
    scope = Object()
    scope.allowance = rate
    scope.last_check = time.time()
    def throttler(fn):
        current = time.time()
        time_passed = current - scope.last_check;
        scope.last_check = current;
        scope.allowance = scope.allowance + time_passed * (rate / per)
        if (scope.allowance > rate):
          scope.allowance = rate
        if (scope.allowance < 1):
          pass
        else:
          fn()
          scope.allowance = scope.allowance - 1
    return throttler