background
When calling a third-party interface, the common problem is that the calling frequency is too fast, which leads to a series of problems: it may be blocked by IP; it may be blocked; it may also be restricted and other problems.
Solutions
The easiest way: limit the frequency of calls within a time period
The ratelimit implementation controls the frequency of calling the API
For example, the following test code uses multi-threaded simulation request to call test2()
the function
import time
import threading
def test2():
time.sleep(3)
print('调用了函数')
def run_func():
print('模拟普通请求...')
test2()
if __name__ == '__main__':
all_t = []
for i in range(10):
t = threading.Thread(target=run_func)
all_t.append(t)
for n in all_t:
n.start()
for k in all_t:
k.join()
In the above test code, the function is called at the same time in the 10 started threads test2()
, which can be understood as requesting the API 10 times within 1 second. Next, we need to introduce ratelimit
the library to control the frequency of calls.
import time
from ratelimit import limits
@limits(calls=1, period=1)
def test2():
time.sleep(3)
print('调用了函数')
By default, 15 requests are allowed within 15 minutes, and excess requests will be discarded. Setting it here calls=1,period=1
means that only one request is allowed within 1s.
However, when running the above code, an error occurred in some of the threads ratelimit.exception.RateLimitException: too many calls
. Why?
increase blocking wait
As mentioned above, when the number of requests exceeds the set value within the set time, other requests will be discarded. Therefore, RateLimitException
the case where an error is returned needs to be handled.
Add a decorator to block the excess request
import time
from functools import wraps
from ratelimit import RateLimitException
def sleep_and_retry(func):
@wraps(func)
def wrapper(*args, **kargs):
while True:
try:
return func(*args, **kargs)
except RateLimitException as exception:
time.sleep(exception.period_remaining)
return wrapper
result
After adding the above decorator, the excess requests are also blocked, and the requests can be responded normally.
full code
import threading
import time
from functools import wraps
from ratelimit import limits, RateLimitException
def sleep_and_retry(func):
@wraps(func)
def wrapper(*args, **kargs):
while True:
try:
return func(*args, **kargs)
except RateLimitException as exception:
time.sleep(exception.period_remaining)
return wrapper
@sleep_and_retry
@limits(calls=1, period=1)
def test2():
time.sleep(3)
print('调用了函数')
def run_func():
print('模拟普通请求...')
test2()
if __name__ == '__main__':
all_t = []
for i in range(10):
t = threading.Thread(target=run_func)
all_t.append(t)
for n in all_t:
n.start()
for k in all_t:
k.join()