Django 4.x 高并发必须用 ASGI 部署+异步视图+CONN_MAX_AGE=0,WSGI 下 async def 视图会被线程池包装成同步执行,无实际并发提升;ASGI 模式下 ORM 调用须用 sync_to_async 包装,数据库连接池需交由外部(如 pgbouncer)管理。

直接上结论:Django 4.x 要真正发挥高并发能力,必须用 ASGI 部署 + 异步视图 + 合理连接池配置;只换服务器(比如改用 uvicorn)但保留同步代码和默认 CONN_MAX_AGE=0,性能提升几乎为零。
为什么不能继续用 WSGI 部署 Django 4 的异步视图?
WSGI 协议本身是同步的,它要求每个请求都阻塞等待响应返回。即使你在 views.py 里写了 async def 视图,Django 在 WSGI 模式下会自动把它包装进线程池执行——本质还是同步调度,还多一层开销。
现象很典型:async def api_view(request): await asyncio.sleep(1); return JsonResponse({}) 在 WSGI 下压测时,并发数一上去,延迟就线性增长,QPS 上不去。
必须满足两个前提才启用异步路径:
立即学习“Python免费学习笔记(深入)”;
- Django 进程由 ASGI 服务器启动(如
uvicorn、granian、daphne) - ASGI 服务器加载的是
myproject.asgi:application,不是myproject.wsgi:application
ASGI 部署时 CONN_MAX_AGE 必须设为 0
Django 4.2+ 在 ASGI 模式下,数据库连接不能复用。因为 ASGI 事件循环是单线程(或协程共享线程),而 Django 的连接池设计基于线程局部存储(threading.local)。若设 CONN_MAX_AGE=60,多个协程可能误用同一连接,触发 django.db.utils.InterfaceError: connection already closed 或静默数据错乱。
正确做法只有这一种:
python-docx Skill功能概述python-docx Skill是一项面向实际任务的技能,主要用于本Skill提供使用python-docx生成专业Word文档的标准方法和最佳实践;生成安全服务方案文档;核心要点生成技术架构设计文档;生成任何需要专业排版的Word文档;核心库 : python-docx;使用与执行辅助库 : docx.shared , docx.enum , docx.oxml.ns;标准代码模板;1. 文档初始化;2. 字体设置(必须!它将相关步骤、工具调用和结果整理方式集
- 在
settings.py中显式写死:CONN_MAX_AGE = 0 - 把连接池交给 ASGI 服务器或外部组件管理(例如用
pgbouncer做 PostgreSQL 连接池,或用granian内置的连接复用) - 如果用了 Celery,它的 worker 仍是同步线程模型,可单独保留
CONN_MAX_AGE=60(但需确保与 ASGI 进程隔离)
选 uvicorn 还是 granian?看你的瓶颈在哪
uvicorn 是最通用的选择,生态成熟、调试方便,适合大多数项目:
uvicorn myproject.asgi:application --host 0.0.0.0:8000 --workers 4 --reload
但如果你的瓶颈在静态文件或 CPU-bound 请求(比如大量 JSON 序列化/反序列化),granian 更值得考虑——它用 Rust 实现 HTTP 层,自带静态文件服务,实测在同等机器上 QPS 可高出 3–4 倍(见腾讯云 2C2G 测试数据):
granian --interface asgi --host 0.0.0.0 --port 8000 --static-path-route /static --static-path-mount ./static-dist myproject.asgi:application
注意两个易错点:
-
granian默认不读取DJANGO_SETTINGS_MODULE,必须提前export DJANGO_SETTINGS_MODULE=myproject.settings.production - 它不兼容某些依赖线程局部变量的中间件(如老版本
django-debug-toolbar),上线前务必关闭或替换
异步视图里调用 ORM 必须加 @sync_to_async
Django 的 ORM 全部是同步阻塞的,直接在 async def 里写 User.objects.get(id=1) 会导致整个事件循环卡住,等同于没异步。
正确姿势只有两种:
- 对简单查询,用
@sync_to_async包装(适用于低频、非核心路径):from asgiref.sync import sync_to_async<br>@sync_to_async<br>def get_user_async(user_id):<br> return User.objects.get(id=user_id)<br><br>async def my_view(request):<br> user = await get_user_async(1)<br> return JsonResponse({"name": user.name}) - 对高频 I/O 密集操作(如调用外部 API + 缓存),优先用原生异步库(
httpx.AsyncClient、aioredis),绕过 ORM
别试图给 QuerySet 加 await——它不是协程对象,也不会变成。

















