Когда все сопрограммы ждут, Asyncio прослушивает события, чтобы разбудить их снова. Типичным примером будет asyncio.sleep()
, который регистрирует синхронизированное событие. На практике событие обычно представляет собой сокет ввода-вывода, готовый для приема или отправки новых данных.
Чтобы лучше понять это поведение, я настроил простой тест: он отправляет 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?