Python async - 所有 aiohttp 请求立即发送

Python async - all aiohttp request get sent at once

我正在使用 python 3.7 和 aiohttp,试图从客户端向服务器发送异步 http 请求。 这是服务器代码:

import asyncio
from aiohttp import web


async def hello(request):
    print('Got request')
    await asyncio.sleep(2)
    headers = {"content_type": "text/html"}
    response = web.Response(body='Hello', headers=headers)
    return response

app = web.Application()
app.router.add_route("GET", "/", hello)
web.run_app(app)

这是客户端代码:

import asyncio
from aiohttp import ClientSession
import time


async def fetch(url, session):
    print('Starting request')
    # some blocking calculation
    time.sleep(0.3)
    async with session.get(url) as response:
        print('Finished request')


async def run(r):
    url = "http://localhost:8080"
    tasks = []
    start = time.time()
    async with ClientSession() as session:
        for i in range(r):
            task = asyncio.create_task(fetch(url, session))
            tasks.append(task)

        responses = await asyncio.gather(*tasks)
    print(time.time()-start)

asyncio.run(run(10))

但是我有一个问题,看起来所有的请求都是 'prepared' 一个,然后一次发送。

这是输出的打印方式,其中 "blocking calculation" 在 for "fetch" 函数中:gif1

这就是在 for 循环中完成 "blocking calculation" 的地方:gif2

我有两个问题。
1. 是什么导致了 gif1 和 2 之间的行为差​​异?
2.为什么所有请求都一次发送?我希望输出类似于此:

开始请求
开始请求
开始请求
完成请求
完成请求
开始请求
完成请求
...

问题在于阻塞代码(例如对 time.sleep(0.3) 的调用)无法由 asyncio 并行执行,因为它会阻塞整个事件循环线程。将其替换为 await asyncio.sleep(0.3),问题就会消失。

如果您有必须在协程中 运行 的实际阻塞代码(例如 numpy 计算),请在侧线程中使用 await loop.run_in_executor(None, blocking_function) 到 运行 计算并安全地等待结果,允许其他协程在等待时取得进展。