Нужны два независимых ограничения: семафор на 10 активных запросов и ограничитель стартов до 10 за скользящую секунду. Один semaphore не задаёт requests per second.
Асинхронная отправка тысяч запросов с ограничением скорости 10 запросов в секунду
Нужны два независимых ограничения: семафор на 10 активных запросов и ограничитель стартов до 10 за скользящую секунду. Один semaphore не задаёт requests per second.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Разработайте асинхронную функцию do_requests на базе asyncio, которая будет выполнять около 1000 GET‑запросов к разным ресурсам. Параллельно не должно работать более 10 запросов, т.е. необходимо реализовать ограничитель количества одновременно активных запросов. Каждый запрос отличается только уникальным URL, а полученный ответ следует вывести в консоль. Учтите, что внешний сервис отклонит запросы, если их количество превысит 10 в секунду.
async def do_requests(urls: list[str]) -> None:
Ответ
Нужны два независимых ограничения: семафор на 10 активных запросов и ограничитель стартов до 10 за скользящую секунду. Один semaphore не задаёт requests per second.
Пример ядра ограничителя стартов:
import asyncio
from collections import deque
class RateGate:
def __init__(self):
self.starts = deque()
self.lock = asyncio.Lock()
async def wait(self):
async with self.lock:
loop = asyncio.get_running_loop()
while True:
now = loop.time()
while self.starts and self.starts[0] <= now - 1:
self.starts.popleft()
if len(self.starts) < 10:
self.starts.append(now)
return
await asyncio.sleep(self.starts[0] + 1 - now)
В do_requests создайте общий async HTTP-клиент, gate и semaphore. Каждый worker сначала получает semaphore, затем ждёт gate, выполняет GET с timeout и печатает проверенный ответ. Для тысячи URL лучше ограниченная очередь workers, чем неограниченный запуск. Повторные запросы тоже проходят gate; серверный 429 и Retry-After обрабатываются отдельно. Лимит локальный для одного процесса.