python - 同时多个异步请求
问题描述
我正在尝试同时调用 ~ 300 个 API 调用,这样我最多可以在几秒钟内得到结果。
我的伪代码如下所示:
def function_1():
colors = ['yellow', 'green', 'blue', + ~300 other ones]
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
res = loop.run_until_complete(get_color_info(colors))
async def get_color_info(colors):
loop = asyncio.get_event_loop()
responses = []
for color in colors:
print("getting color")
url = "https://api.com/{}/".format(color)
data = loop.run_in_executor(None, requests.get, url)
r = await data
responses.append(r.json())
return responses
这样做我getting color
每隔一秒左右就会打印出来,并且代码需要很长时间,所以我很确定它们不会同时运行。我究竟做错了什么?
解决方案
aiohttp
使用原生协程 ( async
/ await
)
这是一个典型的模式,可以完成你想要做的事情。(Python 3.7+。)
一个主要的变化是您需要从requests
为同步 IO 构建的包迁移到aiohttp
专门为与async
/ await
(本机协程)一起使用而构建的包:
import asyncio
import aiohttp # pip install aiohttp aiodns
async def get(
session: aiohttp.ClientSession,
color: str,
**kwargs
) -> dict:
url = f"https://api.com/{color}/"
print(f"Requesting {url}")
resp = await session.request('GET', url=url, **kwargs)
# Note that this may raise an exception for non-2xx responses
# You can either handle that here, or pass the exception through
data = await resp.json()
print(f"Received data for {url}")
return data
async def main(colors, **kwargs):
# Asynchronous context manager. Prefer this rather
# than using a different session for each GET request
async with aiohttp.ClientSession() as session:
tasks = []
for c in colors:
tasks.append(get(session=session, color=c, **kwargs))
# asyncio.gather() will wait on the entire task set to be
# completed. If you want to process results greedily as they come in,
# loop over asyncio.as_completed()
htmls = await asyncio.gather(*tasks, return_exceptions=True)
return htmls
if __name__ == '__main__':
colors = ['red', 'blue', 'green'] # ...
# Either take colors from stdin or make some default here
asyncio.run(main(colors)) # Python 3.7+
这有两个不同的元素,一个是协程的异步方面,另一个是在指定任务容器(期货)时引入的并发:
- 您创建了一个与两个可等待对象
get
一起使用的协程:第一个存在和第二个存在。这是异步方面。这些 IO-bound 响应的目的是告诉事件循环,其他调用可以轮流通过相同的例程运行。await
.request
.json
await
get()
- 并发方面封装在
await asyncio.gather(*tasks)
. 这会将等待get()
调用映射到您的每个colors
. 结果是返回值的聚合列表。请注意,此包装器将等到您的所有响应都进来并调用.json()
. 或者,如果你想在它们准备好时贪婪地处理它们,你可以循环asyncio.as_completed
:返回的每个 Future 对象代表剩余等待集合中最早的结果。
最后,请注意这asyncio.run()
是 Python 3.7 中引入的高级“瓷器”功能。在早期版本中,您可以(大致)模仿它,例如:
# The "full" versions makes a new event loop and calls
# loop.shutdown_asyncgens(), see link above
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main(colors))
finally:
loop.close()
限制请求
有多种方法可以限制并发率。例如,asyncio.semaphore
在 async-await 函数或大量并发受限的任务中查看。
推荐阅读
- jdbc - QueryRunner 返回“ORA-00972:标识符太长”,将值作为参数传递
- reactjs - 如何模拟 Material-ui withStyles 的实现?
- json - 如何使用 Rest API 从 powershell 获取、删除或发布 wordpress 4.9 帖子?
- json - 如果嵌套数组为空,jq 不返回任何内容
- javascript - 动态更新以编程方式创建的链接(例如 – Blob)
- android - 如何在 Instagram 应用程序上实现视图(不在图像上)的模糊/霜/纤维背景效果
- c# - NetworkInfo.IsConnected 对象引用未设置为 xamarin 上的对象实例
- oculus - VRTK 3.3 Oculus 控制器不跟踪
- datetime - 使用不断变化的时区安排活动
- .net - .NET CLR 版本 v4.0.30319 显示的不同值