如何在Python 3中限制多线程程序中的API调用?

时间:2014-10-05 07:44:21

标签: python multithreading api

经过大量研究,我不确定最佳做法是什么,我的想法是否合适?

我想访问一个API,将我可以拨打的总呼叫数限制为每分钟50次。

我的程序有多个线程独立运行。

如何限制我的程序保持低于阈值?

我的想法是创建一个队列,每隔X秒向它添加一个东西,其中X = thread_count / allowed_calls * 60。然后,需要一个单独的线程来处理这些请求。 (以及定期添加的单独线程)

对于这样的事情,最佳做法是什么?有没有办法实现这一点,而不需要为每个小功能提供完全独立的线程?

2 个答案:

答案 0 :(得分:2)

为什么不创建一个使用内部变量来控制呼叫次数和第一次呼叫的类?

从中删除此代码 https://github.com/lucjon/Py-StackExchange/blob/master/stackexchange/web.py

基本上,它检查您是否拥有超过所需的呼叫数量,如果是这种情况则停止。 如果您正在使用多线程(如池),请将函数请求作为要执行的函数传递。

class WebRequestManager(object):
    # When we last made a request
    window = datetime.datetime.now()
    # Number of requests since last throttle window
    num_requests = 0

    def request(self, url, params):
        now = datetime.datetime.now()

        # Before we do the actual request, are we going to be throttled?
        def halt(wait_time):
            if self.throttle_stop:
                raise TooManyRequestsError()
            else:
                # Wait the required time, plus a bit of extra padding time.
                time.sleep(wait_time + 0.1)

        if (now - WebRequestManager.window).seconds >= 1:
            WebRequestManager.window = now
            WebRequestManager.num_requests = 0

        WebRequestManager.num_requests += 1
        if WebRequestManager.num_requests > 30:
            halt(5 - (WebRequestManager.window - now).seconds)

        request = urllib.request.Request(url)
        ...

答案 1 :(得分:1)

您可以使用限制调用的装饰器:

import time, threading

# make it work nice across threads
def RateLimited(max_per_second):
  '''
  Decorator that make functions not be called faster than
  '''
  lock = threading.Lock()
  minInterval = 1.0 / float(max_per_second)
  def decorate(func):
    lastTimeCalled = [0.0]
    def rateLimitedFunction(args,*kargs):
      lock.acquire()
      elapsed = time.clock() - lastTimeCalled[0]
      leftToWait = minInterval - elapsed

      if leftToWait>0:
        time.sleep(leftToWait)

      lock.release()

      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)