uvicorn性能优于aiohttp服务端,因其asgi服务器定位使http解析(httptools/c层)、连接管理等关键路径深度优化;而aiohttp服务端基于纯python的streamreader,协议栈开销大,且缺乏多进程、平滑重启及asgi生态支持。

Uvicorn 和 aiohttp 都用 uvloop,为什么性能还差一截
因为它们根本不是同一类东西:Uvicorn 是 ASGI 服务器(类似 Nginx + Gunicorn 的角色),aiohttp 既是客户端库,也能当 Web 服务器用,但它的服务端实现没做深度优化。哪怕都用 uvloop,Uvicorn 的 HTTP 解析层默认走 httptools(C 实现),而 aiohttp 服务端用的是纯 Python 的 asyncio.StreamReader,解析开销大得多。
实测中,相同硬件下跑一个空响应接口:
- Uvicorn(
--http httptools --loop uvloop --workers 4):RPS ≈ 21k,P99 延迟 ≈ 0.47ms - aiohttp 服务端(
app = web.Application(); web.run_app(app, loop=uvloop.new_event_loop())):RPS ≈ 6.8k,P99 延迟 ≈ 1.3ms
差距主要来自协议栈深度:Uvicorn 把 ASGI 协议、HTTP/1.1 解析、连接复用、keep-alive 管理全压在 C 层;aiohttp 服务端则把更多逻辑留在 Python 层,调度和内存拷贝更重。
aiohttp 做客户端时,和 Uvicorn 启动的 FastAPI 服务配对有没有坑
有,而且很常见——不是协议问题,是超时和连接池配置不匹配导致的“伪慢”。比如 FastAPI 用 Uvicorn 启动时默认 --timeout-keep-alive 5(单位秒),而 aiohttp 客户端默认 timeout=5 且不启用连接池复用,一旦并发请求多,就会频繁重建 TCP 连接,触发 TIME_WAIT 拥塞。
关键修复点:
- Uvicorn 启动加
--timeout-keep-alive 60,让后端长连接保持更久 - aiohttp 客户端必须显式配置
connector = aiohttp.TCPConnector(keepalive_timeout=60) - 避免在每次请求里新建
ClientSession,应复用 session 实例 - 如果调用的是本机服务(如
http://localhost:8000),记得关掉 aiohttp 的 DNS 缓存:resolver = aiohttp.AsyncResolver()→ 不推荐;改用aiodns或直接禁用
什么时候该选 aiohttp 服务端而不是 Uvicorn
基本只有一种情况:你需要 WebSocket 连接生命周期内混用长轮询、SSE、二进制帧、自定义协议升级,且要精细控制每个连接的底层 socket 行为。比如做 MQTT over WebSocket 网关、实时音视频信令中继、或需要 transport.write_eof() 精确控制流结束的场景。
Uvicorn 0.52.1 是 Python ASGI Web 服务器,提供异步应用服务及 WebSocket 支持,常用于 FastAPI、Starlette 等应用的开发和部署。本下载为官方 PyPI 通用 wheel 包,可使用 pip 安装,并由包管理器处理所需依赖。
除此之外,别选 aiohttp 做服务端。它没有 worker 进程模型,没法利用多核;没有平滑 reload;不支持 ASGI 中间件生态(比如 starlette.middleware.cors);日志和错误追踪也弱于 Uvicorn+FastAPI 组合。
简单判断标准:if "需要 spawn subprocess per request" or "require low-level socket control": use aiohttp server else: use uvicorn
Uvicorn 的 --workers 和 aiohttp 的 asyncio.create_task 并发数怎么对齐
不能对齐,也不该对齐。这是两个层级的并发控制:
-
--workers是进程级并行,每个 worker 是独立的 Python 进程 + 独立事件循环,适合 CPU-bound 或阻塞 IO 场景隔离 -
asyncio.create_task()是协程级并发,在单个事件循环内调度,只适用于纯异步 IO(如 HTTP 请求、Redis 调用)
典型误用:在 Uvicorn 的单个 worker 里开 1000 个 create_task() 去调第三方 API,结果被对方限流或触发连接数上限——这不是 Uvicorn 的问题,是没配 limit-concurrency 和 client-side connector 限流。
生产建议:
- Uvicorn
--workers设为$(nproc) + 1(最多别超 8) - aiohttp 客户端用
semaphore = asyncio.Semaphore(100)控制并发请求数 - 必要时在 Uvicorn 启动参数里加
--limit-concurrency 1000防止单 worker 过载
最易被忽略的一点:Uvicorn 的 --timeout-graceful-shutdown 必须大于 aiohttp 客户端最长可能耗时,否则进程重启时正在跑的异步请求会被静默丢弃,连 cancel 信号都收不到。










