aiohttp: ограничение скорости параллельных запросов
API часто имеют ограничения скорости, которые пользователи должны соблюдать. В качестве примера возьмем 50 запросов в секунду. Последовательные запросы занимают 0,5-1 секунду и поэтому слишком медленны, чтобы приблизиться к этому пределу. Параллельные запросы с aiohttp, однако, превышают ограничение скорости.
Чтобы опросить API настолько быстро,насколько это возможно, нужно ограничить скорость параллельных вызовов.
Примеры, которые я нашел до сих пор украшают session.get
, примерно так:
session.get = rate_limited(max_calls_per_second)(session.get)
Это хорошо работает для последовательных вызовов. Пытаюсь реализация этого в параллельных вызовах не работает должным образом.
Вот пример кода:
async with aiohttp.ClientSession() as session:
session.get = rate_limited(max_calls_per_second)(session.get)
tasks = (asyncio.ensure_future(download_coroutine(
timeout, session, url)) for url in urls)
process_responses_function(await asyncio.gather(*tasks))
Проблема с этим заключается в том, что он будет ограничивать скорость очереди задач. Выполнение с gather
все равно произойдет более или менее одновременно. Худший из обоих миров ; -).
Да, я нашел аналогичный вопрос прямо здесь aiohttp: установите максимальное количество запросов в секунду , но ни один из ответов не отвечает на фактический вопрос об ограничении скорости запросы. Такжесообщение в блоге от Quentin Pradet работает только на ограничение скорости очереди.
Подводить итоги: как можно ограничить количество запросов в секунду для параллельного aiohttp
просит?
1 ответ:
Если я вас хорошо понимаю, вы хотите ограничить количество одновременных запросов?
Внутри
asyncio
есть объект с именемSemaphore
, он работает как асинхронныйRLock
.semaphore = asyncio.Semaphore(50) #... async def limit_wrap(url): async with semaphore: # do what you want #... results = asyncio.gather([limit_wrap(url) for url in urls])
Обновлено
Предположим, я делаю 50 одновременных запросов, и все они заканчиваются за 2 секунды. Таким образом, это не касается ограничения(только 25 запросов в секунду). Это означает, что я должен сделать 100 одновременных запросов, и все они заканчиваются через 2 секунды(50 запросов в секунду). Но перед тобой на самом деле сделайте эти запросы, как вы можете определить, как долго они закончатся?Или, если вы не возражаете, законченные запросы в секунду , но запросы, сделанные в секунду. Вы можете:
async def loop_wrap(urls): for url in urls: asyncio.ensure_future(download(url)) await asyncio.sleep(1/50) asyncio.ensure_future(loop_wrap(urls)) loop.run_forever()
Приведенный выше код будет создавать экземпляр
Future
каждые1/50
секунды.