我正在学习异步和信号量,我已经使用fastapi做了一个端点来测试。fastapi端点只是一个简单的服务器端,它接受一个包含睡眠时间的请求,并在返回响应之前长时间睡眠。我通过uvicorn运行fastapi进行测试,但有5名工作人员。这只是为了测试,我知道在生产中我应该使用gunicorn和nginx,但为了学习,我只是使用uvicorn
uvicorn api_example:app --port 8008 --host cdsre.co.uk --workers 5
api_示例.py
from time import sleep, time
import json
from fastapi import FastAPI, Response, Request
app = FastAPI()
@app.post("/sleeper")
async def sleeper(request: Request):
request_data = await request.json()
sleep_time = request_data['sleep_time']
start = time()
sleep(sleep_time)
end = time()
return Response(content=json.dumps({"slept for": end - start}))
我的本地机器上的客户端代码试图利用异步和信号量来并行调用3个post请求。我有6个请求,睡眠计时器为5秒。因此,这里的期望是处理这6个请求大约需要10秒
异步\u示例.py
import aiohttp
import asyncio
import time
async def get_http_response(session, url):
async with semaphore:
print("firing request...")
start = time.time()
async with session.post(url, json={"sleep_time": 5}) as resp:
response = await resp.text()
end = time.time()
print(f"Client time: {end - start}, server time: {response}")
return response
async def main():
async with aiohttp.ClientSession() as session:
tasks = []
for number in range(6):
url = f'http://cdsre.co.uk:8008/sleeper'
tasks.append(asyncio.ensure_future(get_http_response(session, url)))
responses = await asyncio.gather(*tasks)
for response in responses:
pass
# print(response)
semaphore = asyncio.Semaphore(3)
start_time = time.time()
asyncio.get_event_loop().run_until_complete(main())
print("--- %s seconds ---" % (time.time() - start_time))
然而,post请求通常需要两倍于它应该花费的时间。在这种情况下,睡眠计时器为5秒,一些请求需要10秒
firing request...
firing request...
firing request...
Client time: 5.137275695800781, server time: {"slept for": 5.005105018615723}
firing request...
Client time: 10.158655643463135, server time: {"slept for": 5.0042970180511475}
Client time: 10.158655643463135, server time: {"slept for": 5.001959800720215}
firing request...
firing request...
Client time: 5.055504560470581, server time: {"slept for": 5.005110025405884}
Client time: 5.056135654449463, server time: {"slept for": 5.005115509033203}
Client time: 5.107320070266724, server time: {"slept for": 5.005107402801514}
--- 15.271023750305176 seconds ---
有时它的速度是我的3倍,这是我睡眠时间的一个因素,这让我觉得有某种排队发生了,或者我错过了某种比赛条件,然而,我认为信号量模式的全部目的是避免这些竞争条件,这样我在任何时间限制为3个请求总是会少于服务器端(5个工作线程)上可用的工作线程,因此应该始终有一个可用的服务器端来处理它
我也不会开始计时,直到在信号灯内,所以我不会提前启动它,所以它应该只在发送请求时启动计时器。希望我只是错过了一些明显的东西。如果有人想试试的话,我已经把端点url放上去了。如果能帮我解决这个问题,我将不胜感激。本质上,我需要能够编写一个异步客户机,它可以并行发送请求,并在测量响应时间时保持一致
Exmaple,有些需要3倍的时间
firing request...
firing request...
firing request...
Client time: 15.127191305160522, server time: {"slept for": 5.001192808151245}
firing request...
Client time: 15.127155303955078, server time: {"slept for": 5.005094766616821}
Client time: 15.127155303955078, server time: {"slept for": 5.005074977874756}
firing request...
firing request...
Client time: 5.053789854049683, server time: {"slept for": 5.005076169967651}
Client time: 5.100871801376343, server time: {"slept for": 5.005076885223389}
Client time: 10.107984781265259, server time: {"slept for": 5.005110502243042}
--- 25.236175775527954 seconds ---
继续评论:
虽然我没有检查默认情况下uvunicorn是如何设置其工作线程的,但使用async进行编码的关键是在同一线程上完成所有工作,或者尽可能多地完成所有工作。 由于您的视图被定义为
async
,这是特别正确的-此编码范例不期望异步函数在等待任何调用时阻塞-线程将停止如果您不需要调用} 调用使这变得很容易:它将自动创建一个带有线程池的基于线程的执行器,并在一个单独的线程中运行阻塞函数,同时释放异步事件循环的当前线程以进一步协调其他任务/工作者
sleep
,而需要调用一些需要时间的代码,并且这些代码不是异步的,那么您可以通过在单独的线程中运行委托函数来实现异步调用。Python asyncio通过提供^{否则,如前所述,如果您使用
sleep
进行测试,只需等待asyncio.sleep
。您可以使用await loop.run_in_executor(None, time.sleep, 5)
来检查它在阻塞函数中的行为相关问题 更多 >
编程相关推荐