poi*_*rez 2 python python-asyncio fastapi uvicorn
了解 Uvicorn 异步行为
我试图了解 Uvicorn 的行为。我创建了一个示例 fastapi 应用程序,主要休眠 5 秒。
import time
from datetime import datetime
from fastapi import FastAPI
app = FastAPI()
counter = 0
@app.get("/")
def root():
global counter
counter = counter + 1
my_id = counter
print(f'I ({my_id}) am feeling sleepy')
time.sleep(5)
print(f'I ({my_id}) am done sleeping')
return {}
Run Code Online (Sandbox Code Playgroud)
我使用 Apache Bench 的以下命令调用我的应用程序:
ab -n 5 -c 5 http://127.0.0.1:8000/
Run Code Online (Sandbox Code Playgroud)
输出:
I (1) am feeling sleepy -- 0s
I (1) am done sleeping -- 5s
I (2) am feeling sleepy -- 5s
I (3) am feeling sleepy -- 5s
I (4) am feeling sleepy -- 5s
I (5) am feeling sleepy -- 5s
I (2) am done sleeping -- 10s
I (4) am done sleeping -- 10s
I (3) am done sleeping -- 10s
I (5) am done sleeping -- 10s
Run Code Online (Sandbox Code Playgroud)
为什么请求会同时运行?我运行该应用程序为:
uvicorn main:app --workers 1
Run Code Online (Sandbox Code Playgroud)
请注意,我没有使用 async 关键字,所以对我来说一切都应该完全同步。
来自 FastAPI 文档:
当您使用普通 def 而不是异步 def 声明路径操作函数时,它会在等待的外部线程池中运行,而不是直接调用(因为它会阻塞服务器)。
这个线程池在哪里?当我使用睡眠时,我认为唯一可用的工作人员将被完全阻止。
FastAPI 使用 Starlette,而 Starlette 在幕后使用 AnyIO。默认提供40个线程的线程池来处理同步请求。这个线程池是并发同步请求的多次执行的魔力背后的原因。
可以这样配置:
from anyio.lowlevel import RunVar
from anyio import CapacityLimiter
app = FastAPI()
@app.on_event("startup")
def startup():
print("start")
RunVar("_default_thread_limiter").set(CapacityLimiter(2))
Run Code Online (Sandbox Code Playgroud)
来源:https ://github.com/tiangolo/fastapi/issues/4221#issuecomment-982260467
感谢@MatsLindh。