Uvicorn采用单进程+协程的异步并发模型,依赖asyncio事件循环在单个OS进程中调度成千上万个协程,IO等待时不阻塞线程,而是交还控制权给事件循环;它不靠多线程或预分叉支撑并发,因GIL限制和多进程开销大,故将并发交给协程、横向扩展交由gunicorn等上层管理。

Uvicorn用的是单进程+协程的异步并发模型
它不靠多线程或预分叉(pre-fork)来撑并发,而是依赖 Python 的 asyncio 事件循环,在一个 OS 进程内调度成千上万个协程。每个 HTTP 连接对应一个协程,IO 等待时不阻塞整个线程,而是把控制权交还给事件循环去处理其他就绪任务。
为什么不用多线程或多进程做并发主力
因为 Uvicorn 的设计目标是轻量、低开销、高吞吐的 IO 密集型服务。多线程在 Python 里受 GIL 限制,对纯 IO 场景提升有限;多进程则带来内存复制、IPC、信号管理等额外负担。Uvicorn 把“并发”这件事交给协程层解决,把“横向扩展”留给上层(比如用 gunicorn 启动多个 Uvicorn worker)。
Uvicorn 0.52.1 是 Python ASGI Web 服务器,提供异步应用服务及 WebSocket 支持,常用于 FastAPI、Starlette 等应用的开发和部署。本下载为官方 PyPI 通用 wheel 包,可使用 pip 安装,并由包管理器处理所需依赖。
- 默认启动方式
uvicorn main:app是单进程单事件循环,只跑一个协程调度器 - 加
--workers N并不会让 Uvicorn 自己 fork——那是gunicorn的行为;Uvicorn 本身不实现进程管理 - 如果你直接用
uvicorn --workers 4,会报错:Workers not supported in default mode - 真要多进程,得用
gunicorn -k uvicorn.workers.UvicornWorker这种组合
协程调度实际怎么影响请求处理
关键在于:一个协程从 accept 连接到读 header、收 body、调用应用、写响应,全程都在同一个事件循环里流转。中间任何一步遇到 await(比如 await request.json() 或 await database.fetch_one(...)),当前协程就挂起,事件循环立刻切到另一个就绪的协程。
- 如果应用里混用了同步阻塞调用(如
time.sleep(1)、requests.get()、没加await的数据库操作),那个协程会卡死整个事件循环,所有请求都会延迟 -
httptools解析器是 C 实现的,解析请求极快,避免在协议层拖慢协程切换 - 连接空闲时(比如 SSE 或长轮询),协程只是等待 receive 事件,不消耗 CPU,但保持 socket 打开
容易被忽略的并发边界
很多人以为“Uvicorn 能并发 10000 连接”,就等于能同时处理 10000 个耗时请求——其实不是。真实并发能力取决于三件事:事件循环调度效率、应用内部 await 点是否合理、以及底层系统资源(文件描述符数、内存、网络缓冲区)。
-
ulimit -n太低会导致Too many open files错误,哪怕协程再多也连不上 - 数据库连接池大小必须和协程数匹配,否则大量协程会卡在
await pool.acquire() - 用
uvloop替换默认 asyncio 事件循环,能在高连接数下提升 10%–20% 吞吐,但调试难度上升 - WebSocket 连接长期存活,会持续占用协程+socket,比短连接更吃资源

















