python - asyncio + aiohttp:重叠 IO 与睡眠
问题描述
当所有协程都在等待时,asyncio 会监听事件以再次唤醒它们。一个常见的例子是asyncio.sleep()
,它注册了一个定时事件。在实践中,事件通常是一个准备好接收或发送新数据的 IO 套接字。
为了更好地理解这种行为,我设置了一个简单的测试:它向 localhost 发送一个 http 请求并等待响应。在 localhost 上,我设置了一个烧瓶服务器,它在响应之前等待 1 秒。发送请求后,客户端休眠 1 秒,然后等待响应。我希望这会在大约一秒钟内返回,因为我的程序和服务器都应该并行休眠。但这需要2秒:
import aiohttp
import asyncio
from time import perf_counter
async def main():
async with aiohttp.ClientSession() as session:
# this http request will take 1 second to respond
async with session.get("http://127.0.0.1:5000/") as response:
# yield control for 1 second
await asyncio.sleep(1)
# wait for the http request to return
text = await response.text()
return text
loop = asyncio.get_event_loop()
start = perf_counter()
results = loop.run_until_complete(main())
stop = perf_counter()
print(f"took {stop-start} seconds") # 2.01909
asyncio 在这里做什么,为什么我不能重叠等待时间?
我对HTTP请求的具体场景不感兴趣,aiohttp只是用来构造一个例子。这可能有点危险:这可能与 aiohttp 有关,而根本与 asyncio 无关。
实际上,我希望是这种情况(因此关于 asyncio 和 aiohttp 的问题标题)。我的第一个直觉是在调用之前可能没有发送请求asyncio.sleep().
所以我重新排序了一些东西:
# start coroutine
text = response.text()
# yield control for 1 second
await asyncio.sleep(1)
# wait for the http request to return
text = await text
但这仍然需要两秒钟。
好的,现在要确定请求是在睡觉前发送的,我print("incoming")
在服务器上的路由中添加了它,然后才进入睡眠状态。我还在客户端上将睡眠时间的长度更改为 10 秒。服务器在客户端运行后立即打印传入。客户端总共需要 11 秒。
@app.route('/')
def index():
print("incoming")
time.sleep(1)
return 'done'
由于 HTTP 请求是立即发出的,因此在客户端醒来之前,服务器肯定已经发送了一个答案。在asyncio.sleep().
我看来,提供 HTTP 请求的套接字应该在客户端醒来后立即准备就绪。但是,总运行时间始终是客户端和服务器等待时间的增加。
我是否以某种方式滥用了 asyncio,或者这毕竟与 aiohttp 有关?
解决方案
问题是一秒钟发生在服务器中执行async with session.get("http://127.0.0.1:5000/") as response:
。
http 请求在您获得此response
对象之前完成。
您可以通过以下方式对其进行测试:
...
async def main():
async with aiohttp.ClientSession() as session:
start = perf_counter()
# this http request will take 1 second to respond
async with session.get("http://127.0.0.1:5000/") as response:
end = perf_counter()
print(f"took {end-start} seconds to get response")
# yield control for 1 second
await asyncio.sleep(1)
# wait for the http request to return
text = await response.text()
return text
...
顺便说一句,只要你有另一个正在运行的协程,你肯定可以重叠这个等待时间。
推荐阅读
- c# - 如何从 WPF C# 应用程序的主线程调用将 UI 元素加载到不同堆栈面板的函数
- r - 了解 R 中树(data.tree)的递归构建
- ios - Xcode 10 阿拉伯语(RTL)故事板未加载
- migration - 从不同来源和架构到 MS SQL 服务器的数据迁移
- python - 创建 Python XML 到 JSON 转换器?
- entity-framework - 使用列表过滤的 linq 查询
(健康)状况 - javascript - 如何计算 div 元素的旋转角度?
- c++ - 将 Lua 函数块转换为 C 字符串
- c++ - 当我的软件使用 AddressSanitizer 编译时,如何随时输出分配的内存缓冲区列表?
- ios - user_first_touch_timestamp 与 first_open_time 有何不同?