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))
这样做的问题是它会对任务的排队进行速率限制.使用聚集执行仍然会或多或少同时发生.最糟糕的两个世界;-).
是的,我在aiohttp: set maximum number of requests per second找到了一个类似的问题,但是没有回复回答限制请求率的实际问题.此外,the blog post from Quentin Pradet仅适用于限速排队.
包装起来:如何限制并行aiohttp请求的每秒请求数?
解决方法:
如果我理解你,你想限制同时请求的数量?
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()
上面的代码将每1/50秒创建一个Future实例.