我需要发送大量的HTTP请求,等待所有请求返回后,程序才能继续执行。这似乎是
在网上搜索后,我找到了两种解决方案:
客户是我的基准。
因此,使用
asyncio
的完美应用场景。我有点天真地将对requests
的调用封装在一个async
函数中,并将其传递给asyncio
。但是这种方式行不通。在网上搜索后,我找到了两种解决方案:
- 使用像aiohttp这样的库,专门为与
asyncio
配合使用而设计。 - 将阻塞代码包装在一个对
run_in_executor
的调用中。
from flask import Flask
import time
app = Flask(__name__)
@app.route('/')
def hello_world():
time.sleep(0.1) // heavy calculations here :)
return 'Hello World!'
if __name__ == '__main__':
app.run()
客户是我的基准。
import requests
from time import perf_counter, sleep
# this is the baseline, sequential calls to requests.get
start = perf_counter()
for i in range(10):
r = requests.get("http://127.0.0.1:5000/")
stop = perf_counter()
print(f"synchronous took {stop-start} seconds") # 1.062 secs
# now the naive asyncio version
import asyncio
loop = asyncio.get_event_loop()
async def get_response():
r = requests.get("http://127.0.0.1:5000/")
start = perf_counter()
loop.run_until_complete(asyncio.gather(*[get_response() for i in range(10)]))
stop = perf_counter()
print(f"asynchronous took {stop-start} seconds") # 1.049 secs
# the fast asyncio version
start = perf_counter()
loop.run_until_complete(asyncio.gather(
*[loop.run_in_executor(None, requests.get, 'http://127.0.0.1:5000/') for i in range(10)]))
stop = perf_counter()
print(f"asynchronous (executor) took {stop-start} seconds") # 0.122 secs
#finally, aiohttp
import aiohttp
async def get_response(session):
async with session.get("http://127.0.0.1:5000/") as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
await get_response(session)
start = perf_counter()
loop.run_until_complete(asyncio.gather(*[main() for i in range(10)]))
stop = perf_counter()
print(f"aiohttp took {stop-start} seconds") # 0.121 secs
因此,使用
asyncio
的直观实现不能处理阻塞的io代码。但是如果正确使用 asyncio
,它与特殊的 aiohttp
框架一样快。关于协程和任务的文档并没有真正提到这一点。只有当你阅读loop.run_in_executor()时,它才会说:
# File operations (such as logging) can block the
# event loop: run them in a thread pool.
我对这种行为感到惊讶。asyncio的目的是加速阻塞io调用。为什么需要另一个包装器run_in_executor来实现这一点呢?
aiohttp的整个卖点似乎在于支持asyncio。但是据我所见,只要将其包装在执行程序中,requests模块就可以完美地工作。是否有避免包装某些内容的原因?
asyncio
使用非阻塞套接字,因此它可以使用一个线程请求多个,但requests
不行。 - KC.