Python异步-一次发送所有aiohttp请求

Python异步-一次发送所有aiohttp请求,python,asynchronous,python-asyncio,aiohttp,Python,Asynchronous,Python Asyncio,Aiohttp,我正在使用python 3.7和,试图将异步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='Hell

我正在使用python 3.7和,试图将异步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))
然而,我有一个问题,看起来所有的请求都在一次一个地“准备”,然后一次发送所有的请求

这是输出的打印方式,其中“块计算”位于for“fetch”funct的内部:

这就是在for循环内部进行“阻塞计算”的方式:

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

启动请求
启动请求
启动请求
已完成请求
已完成请求
启动请求
已完成请求

...

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


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

但是其他协同程序需要做些什么?我想发送http请求,然后才释放对协同路由的控制。“进度”只是在等待服务器在2之后应答seconds@RonSerruya首先,进展实际上是开始执行它们中的任何一个。请注意,在
gif1
中,在“启动请求”打印输出之间有0.3s的暂停,这是因为每个协同程序在开始执行下一个循环之前会阻塞事件循环
time.sleep()
不是一个asyncio函数,因此它不会挂起协程,也不允许asyncio知道睡眠并将睡眠并行化为单个睡眠。由于协同路由有效地以串联方式执行,它们也以串联方式发送请求。另一件事是,事件循环本身必须有机会运行以获取数据,如果数据卡在
time.sleep()
,则不会发生这种情况。好的,我有一个客户端需要执行阻塞计算,将请求发送到服务器,服务器将在几秒钟后返回答案。当我在等待答案的时候,我想计算更多的数据并发送更多的请求。我是否需要将所有计算都放在
运行中\u excecutor
?我的意思是,
print
append
不是异步的,但它们仍然可以吗@user4815162342@RonSerruya是的,任何可能需要相当长时间的操作都应该使用
run\u in\u executor
卸载到线程/进程池中。什么构成了相当长的时间并没有精确的定义,但一般认为像
append
这样的“琐碎”东西可以安全地立即执行。请谨慎对待您的协同程序,就像对待Twisted(或其他异步框架)回调一样。如果某个同步操作看起来不属于异步回调,请将其包装在
run\u in\u executor
-中,或将其重构为异步,例如将
time.sleep
更改为
asyncio.sleep