Flask 和 WSGI 是什么关系?Flask 能不能支持 ASGI 和异步?
简化版
Flask 传统上是 WSGI 框架,请求由 WSGI Server 调用 Flask 应用对象处理。Flask 可以写 async def 视图,但这不等于整个框架变成 ASGI 高并发模型;需要大量异步 I/O 和长连接场景时,通常应考虑 FastAPI、Starlette、Quart 等 ASGI 原生框架。
详细版
WSGI 是 Python Web 应用和服务器之间的同步接口规范。Gunicorn、uWSGI、mod_wsgi 等服务器接收 HTTP 请求后,把请求环境传给 Flask,Flask 返回响应可迭代对象。Flask 的请求上下文、路由、扩展生态长期围绕 WSGI 工作。
ASGI 是面向异步和长连接的接口规范,能统一处理 HTTP、WebSocket、后台事件等异步通信。它适合高并发 I/O、WebSocket、Server-Sent Events 等场景。
Flask 新版本支持 async view,但在 WSGI 部署下,每个请求仍占用一个 worker,异步视图主要方便在视图内部 await 异步库,不会自动带来 ASGI 那种连接级并发能力。面试回答要区分“能写 async”与“框架/部署模型是 ASGI”。
完整版教学
一、WSGI 解决的是什么问题
早期 Python Web 生态里,服务器和框架如果没有统一接口,每个服务器都要适配每个框架。WSGI 的价值是规定一套同步调用协议:服务器负责接收 HTTP,应用负责根据环境生成响应。Flask 就是一个 WSGI 应用。
简化后的 WSGI 应用长这样:
def app(environ, start_response):
start_response("200 OK", [("Content-Type", "text/plain")])
return [b"hello"]
真实 Flask 应用会把 environ 包装成 request 对象,经过路由匹配、before/after 钩子、视图函数和错误处理,最后生成响应。这个接口的特点是同步:一次请求进入应用后,当前 worker 会按调用栈一路处理到响应返回。
二、Flask 在 WSGI 链路中的位置
生产环境不会直接用 app.run() 扛流量,而是用 Gunicorn、uWSGI 等 WSGI Server。Server 监听端口、管理 worker 进程或线程;Flask 只处理“某个请求来了以后应该返回什么”。
浏览器
|
Nginx
|
Gunicorn / uWSGI
|
Flask WSGI app
|
路由 -> 视图 -> 响应
假设 Gunicorn 启动 4 个 worker,每个 worker 同一时间通常处理一个同步请求。如果某个请求里调用第三方接口阻塞 2 秒,这个 worker 这 2 秒就不能处理别的请求。可以增加 worker 或线程缓解,但这仍然是 WSGI 同步模型的扩容方式。
三、ASGI 为什么出现
WSGI 对传统请求响应很合适,但面对 WebSocket、长轮询、大量慢 I/O 时就显得吃力。ASGI 把应用接口设计成异步消息收发模型,让一个事件循环可以在等待 I/O 时切换去处理其他连接。
ASGI 应用的形态更接近这样:
async def app(scope, receive, send):
await send({
"type": "http.response.start",
"status": 200,
"headers": [(b"content-type", b"text/plain")],
})
await send({
"type": "http.response.body",
"body": b"hello",
})
| 维度 | WSGI | ASGI |
|---|---|---|
| 调用模型 | 同步函数调用 | 异步消息协议 |
| 典型服务器 | Gunicorn、uWSGI | Uvicorn、Hypercorn、Daphne |
| 适合场景 | 普通 HTTP 请求响应 | HTTP、WebSocket、长连接、高并发 I/O |
| 框架代表 | Flask、Django 传统模式 | FastAPI、Starlette、Django Channels、Quart |
四、Flask 的 async view 到底意味着什么
Flask 支持在视图里写 async def,这让你可以调用异步 HTTP 客户端、异步数据库驱动等。但在普通 WSGI 部署下,Flask 需要为这个请求运行异步函数并等待结果,请求占用的 worker 并不会因此释放成“一个 worker 扛上千连接”。
@app.get("/profile")
async def profile():
data = await fetch_user_from_remote()
return {"user": data}
这个写法的收益是视图内部可以使用 async 库,尤其当你已经有异步 SDK 时更方便。它的边界是:Flask 扩展、中间件和部署模型多数仍是同步生态;如果你期望 WebSocket、大量长连接、全链路异步数据库访问,选 ASGI 原生框架会更自然。
面试里最容易混淆的是“语法能 await”和“服务模型是 ASGI”。Flask 支持前者,但默认生态和部署仍主要是 WSGI。
五、怎么选择 Flask、FastAPI 或 Quart
如果项目是后台管理、普通 REST API、内部工具、页面渲染,Flask 的简单、成熟扩展和低心智负担很有优势。如果项目强依赖 OpenAPI、类型校验、异步接口、高并发 I/O,FastAPI 往往更贴合。如果想保留 Flask 风格但需要 ASGI/WebSocket,可以考虑 Quart。
用一个数字化场景对比:接口 A 每次 CPU 计算 30ms,数据库 20ms,同步 Flask worker 增加到 8 个就能稳定处理不少请求;接口 B 每次等待外部 API 2 秒,同时有 1000 个慢连接,纯同步 worker 会被等待占满,ASGI 事件循环更有优势。
选择不是“新的一定好”。同步模型更容易排查调用栈、兼容传统库;异步模型要求全链路 I/O 库配合,否则一个阻塞调用就能卡住事件循环。
六、部署时应该怎么回答
Flask 生产部署常见组合是 Nginx + Gunicorn/uWSGI + 多 worker。worker 数量通常参考 CPU 核数、请求耗时、是否开启线程来压测决定,而不是盲目套公式。静态文件、TLS、反向代理、限流通常交给 Nginx 或平台层。
gunicorn "app:create_app()" -w 4 -b 0.0.0.0:8000
如果要尝试 ASGI 包装 WSGI 应用,可以用适配器让 ASGI Server 承载 WSGI app,但这只是兼容层,不会把 Flask 内部生态自动改造成原生异步框架。真正的异步收益来自框架、服务器、数据库驱动、HTTP 客户端全链路协作。
七、常见误区与追问
- 误区:Flask 支持 async,所以 Flask 已经是 ASGI 框架。 async view 只是视图可 await,默认 Flask 应用模型仍主要围绕 WSGI。
- 误区:用了 Uvicorn 就一定变快。 如果内部还是同步 WSGI 应用或阻塞数据库调用,ASGI Server 不能凭空消除阻塞。
- 误区:WSGI 已经过时,所有项目都该换 ASGI。 普通同步 HTTP 业务用 WSGI 仍然成熟稳定,迁移要看长连接和异步 I/O 需求。
- 追问:Flask 为什么不能直接处理 WebSocket? WSGI 是一次请求一次响应模型,不适合 WebSocket 的双向长连接语义。
- 追问:async view 里能调用 requests 吗? 能调用但会阻塞线程或事件循环,应使用异步客户端如
httpx.AsyncClient,或把阻塞调用放到线程池。 - 追问:Gunicorn worker 数怎么定? 需要结合 CPU、请求耗时、阻塞比例和压测结果;CPU 密集和 I/O 密集的最佳配置不同。
- 追问:Flask 和 FastAPI 怎么选? Flask 偏简洁成熟同步生态;FastAPI 偏类型校验、OpenAPI、ASGI 和异步 I/O。
八、加强记忆
记住一句工程判断:Flask 的根是 WSGI,同步请求响应是主场;ASGI 的根是异步消息协议,长连接和高并发 I/O 是主场。Flask 能写 async view,但不要把它等同于全链路 ASGI。面试回答时按“协议接口 -> 服务器 -> 框架 -> 视图 async 的边界 -> 场景选择”这条线讲,就能把 Flask、WSGI、ASGI 和 FastAPI 的关系说清楚。