29 lines
1.2 KiB
Python
29 lines
1.2 KiB
Python
import asyncio
|
|
import random
|
|
|
|
|
|
class RateLimiter:
|
|
"""Гарантирует минимальный интервал между вызовами wait(), плюс джиттер.
|
|
|
|
Semaphore ограничивает только количество одновременных запросов - можно
|
|
держать его хоть в 1, но всё равно долбить сайт пачками сразу как
|
|
освобождается слот. Этот класс решает именно это: не чаще чем раз
|
|
в min_interval секунд, сколько бы корутин ни стояло в очереди.
|
|
"""
|
|
|
|
def __init__(self, min_interval: float, jitter: float = 0.0):
|
|
self.min_interval = min_interval
|
|
self.jitter = jitter
|
|
self._lock = asyncio.Lock()
|
|
self._last_call = 0.0
|
|
|
|
async def wait(self):
|
|
async with self._lock:
|
|
loop = asyncio.get_event_loop()
|
|
now = loop.time()
|
|
delay = self.min_interval + random.uniform(0, self.jitter)
|
|
sleep_for = delay - (now - self._last_call)
|
|
if sleep_for > 0:
|
|
await asyncio.sleep(sleep_for)
|
|
self._last_call = loop.time()
|