aiohttp: tasa limitando solicitudes paralelas

Las API a menudo tienen límites de velocidad que los usuarios deben seguir. Como ejemplo tomemos 50 peticiones / segundo. Las solicitudes secuenciales toman 0.5-1 segundos y, por lo tanto, son demasiado lentas para acercarse a ese límite. Sin embargo, las solicitudes paralelas con aiohttp superan el límite de velocidad.

Para sondear la API tan rápido como se permita, uno necesita calificar el límite de llamadas paralelas.

Los ejemplos que encontré hasta ahora decoran session.get , aproximadamente así:

 session.get = rate_limited(max_calls_per_second)(session.get) 

Esto funciona bien para llamadas secuenciales. Tratar de implementar esto en llamadas paralelas no funciona como se esperaba.

Aquí hay un código como ejemplo:

 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)) 

El problema con esto es que limitará la puesta en cola de las tareas. La ejecución con gather todavía ocurrirá más o menos al mismo tiempo. Lo peor de ambos mundos ;-).

Sí, encontré una pregunta similar aquí aiohttp: establece el número máximo de solicitudes por segundo , pero ninguna de las respuestas responde a la pregunta real de limitar la tasa de solicitudes. Además, la publicación del blog de Quentin Pradet funciona solo en la limitación de velocidad de la puesta en cola.

Para resumir: ¿Cómo se puede limitar el número de solicitudes por segundo para solicitudes paralelas de aiohttp ?

Si te entiendo bien, ¿quieres limitar el número de solicitudes simultáneas?

Hay un objeto dentro de asyncio llamado Semaphore , funciona como un RLock asíncrono.

 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]) 

actualizado

Supongamos que hago 50 solicitudes simultáneas y todas terminan en 2 segundos. Por lo tanto, no toca la limitación (solo 25 solicitudes por segundo).

Eso significa que debo hacer 100 solicitudes simultáneas, y todas terminan en 2 segundos también (50 solicitudes por segundo). Pero antes de que realices esas solicitudes, ¿cómo podrías determinar cuánto tiempo terminarán?

O si no te importa las solicitudes finalizadas por segundo pero las solicitudes realizadas por segundo . Usted puede:

 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() 

El código anterior creará una instancia Future cada 1/50 segundos.