Reputation: 3806
After much research, I'm not sure what the best practice is, is my following idea decent?
I want to access an API that limits the total number of calls I can make to 50 per minute.
My program has multiple threads operating on their own.
How can I limit my program to stay below the threshold?
My idea is to create a queue, and add one thing to it every X seconds where X = thread_count/allowed_calls*60. Then a separate thread would be needed for handling these requests. (And a separate thread for adding at a regular interval)
What would the best practice be for something like this? Is there a way to achieve this without needing completely separate threads for each little functionality?
Upvotes: 6
Views: 5182
Reputation: 1418
You can use a decorator that limits the calls:
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
Then to use the decorator:
@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)
Upvotes: 2
Reputation: 5356
Why don't you create a class that uses an internal variable to control the number of calls and the first call on that second?
ripped this code from https://github.com/lucjon/Py-StackExchange/blob/master/stackexchange/web.py
Basically, it check if you have more than the amount of calls you need and stop if this is the case. If you are using multithread (like Pool), pass the function request as the funcion to be executed.
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)
...
Upvotes: 5