Python异步-一次发送所有aiohttp请求
我正在使用python 3.7和,试图将异步http请求从客户端发送到服务器。 这是服务器代码: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
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
。