You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask与FastAPI多客户端请求处理差异探究及疑问

为什么FastAPI单Worker处理并发请求时会串行,而Flask开发服务器可以并行?

这个问题的核心在于两个框架默认使用的服务器模型以及阻塞代码对并发的影响,我来拆解一下:

1. Flask开发服务器的默认行为

Flask自带的app.run()启动的是Werkzeug开发服务器,它默认是多线程模式。当你发起多个并发请求时,服务器会为每个请求分配一个独立的线程。由于sleep(10)是阻塞IO操作,它只会阻塞当前线程,其他线程仍然可以处理新的请求——这就是你看到Flask能在睡眠期间处理新请求的原因。

不过要注意:Flask的开发服务器只是为了开发方便,生产环境绝不推荐使用,通常会用Gunicorn+Flask或者uWSGI+Flask来实现生产级并发。

2. FastAPI+Uvicorn默认的单Worker问题

你用uvicorn.run()直接启动FastAPI时,默认是单进程、单Worker的ASGI服务器。关键在于你的路由用了async def定义,但里面调用了sleep(10)这个同步阻塞函数:

  • ASGI服务器依赖事件循环来实现异步并发,事件循环是单线程的。一旦你在异步路由里调用了同步阻塞代码,整个事件循环会被卡住,必须等这个阻塞操作完成(10秒睡眠结束),才能处理下一个请求。

解决单Worker下的并发问题

如果你想在单Worker下让FastAPI也能处理并发,需要把阻塞操作放到线程池里,避免阻塞事件循环。修改你的FastAPI代码:

import uvicorn
from fastapi import FastAPI
from time import sleep
import asyncio

app = FastAPI()

@app.get('/')
async def root():
    print('Sleeping for 10')
    # 把同步阻塞的sleep放到线程池执行,不阻塞事件循环
    await asyncio.to_thread(sleep, 10)
    print('Awake')
    return {'message': 'hello'}

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

这样修改后,单Worker的Uvicorn就能同时处理多个请求了,因为事件循环不会被阻塞,可以在等待sleep的期间处理其他请求。

3. 多Worker模式的作用

你后来测试的用Gunicorn启动多个Uvicorn Worker的方式,本质是用多进程模型实现并发:

  • 每个Worker是独立的进程,拥有自己的事件循环。即使某个Worker的事件循环被阻塞,其他Worker仍然可以处理新的请求,效果和Flask开发服务器的多线程类似,但稳定性和性能更适合生产环境。
  • 你也可以直接用Uvicorn的多Worker参数启动:uvicorn.run(app, host="127.0.0.1", port=8000, workers=4),这样不需要Gunicorn也能实现多进程并发。

总结一下:

  • 你的测试结果差异源于默认服务器的并发模型(Flask多线程 vs FastAPI单Worker单事件循环),以及同步阻塞代码对异步事件循环的影响。
  • 处理同步阻塞代码时,要么用线程池隔离阻塞操作(单Worker),要么用多进程/多Worker来分摊负载。

内容的提问来源于stack exchange,提问作者Justin Yeoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:27:31