バックグラウンド
サードパーティのインターフェイスを呼び出すときの一般的な問題は、呼び出し頻度が速すぎることであり、これにより、IP によってブロックされる可能性がある、ブロックされる可能性がある、制限される可能性があるなど、一連の問題が発生します。
ソリューション
最も簡単な方法: 一定期間内の通話頻度を制限する
ratelimit 実装は、API を呼び出す頻度を制御します。
たとえば、次のテスト コードは、マルチスレッド シミュレーション リクエストを使用してtest2()
関数を呼び出します。
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()
上記のテストコードでは、起動した10個のスレッドで同時に関数が呼び出されておりtest2()
、1秒以内に10回APIをリクエストしていることがわかります。以下では、ratelimit
ライブラリ制御呼び出しの頻度を導入する必要があります。
import time
from ratelimit import limits
@limits(calls=1, period=1)
def test2():
time.sleep(3)
print('调用了函数')
デフォルトでは、15 分以内に 15 件のリクエストが許可され、超過したリクエストは破棄されます。ここで設定すると、calls=1,period=1
1 秒以内に 1 つのリクエストのみが許可されることになります。
ただし、上記のコードを実行すると、一部のスレッドでエラーが発生しましたratelimit.exception.RateLimitException: too many calls
。なぜでしょうか?
ブロッキング待機を増やす
前述の通り、設定時間内にリクエスト数が設定値を超えた場合、他のリクエストは破棄されます。したがって、RateLimitException
エラーが返された場合の対処が必要です。
過剰なリクエストをブロックするデコレータを追加する
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
結果
上記のデコレータを追加すると、過剰なリクエストもブロックされ、リクエストが正常に応答できるようになります。
完全なコード
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()