bigywater.tech
Django · 111

Django 异步 View

把视图写成 async def,请求就并发了吗?不一定,取决于它跑在什么服务器上。这篇用真实的 uvicorn 和 gunicorn 各起一个进程,同样的视图打 20 个并发请求,看哪些组合真的并发。

513 ms
uvicorn 上 async 视图,20 个并发请求(每个等 0.5 s)
10.1 s
gunicorn 同步 worker 上同样的 async 视图:排队
520 ms
意外:uvicorn 上的 sync 视图也没有排队

同一个视图,两种服务器

实测 · 本机执行输出
  uvicorn + async def 视图(asyncio.sleep) 总耗时     513 ms   单个请求最快    503 / 最慢    508 ms   全部 200: True
  uvicorn + 普通 def 视图(time.sleep)    总耗时     520 ms   单个请求最快    509 / 最慢    515 ms   全部 200: True
  gunicorn 同步 worker + async def 视图  总耗时   10072 ms   单个请求最快    504 / 最慢  10068 ms   全部 200: True
  gunicorn 同步 worker + 普通 def 视图     总耗时   10067 ms   单个请求最快    504 / 最慢  10063 ms   全部 200: True

ASGI 下 async 与 sync 视图都是并发的,WSGI 同步 worker 下都是排队的——所以 async def 本身不是并发的原因,ASGI 服务器才是。两个视图要「等」的方式不同:await asyncio.sleep 不占线程,time.sleep 占着线程。在 ASGI 下 sync 视图能并发,是因为 Django 给每个请求分配了单独的线程(第 112、113 篇会用线程名验证)。

这个实验没有说明的事

视图里的「等待」是 sleep,不是真的数据库或网络调用;并发数只有 20;worker 数是 1。sync 视图在 ASGI 下并发数很大时会受线程数量限制,本篇没有测。

分步演示

async 视图与服务器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。uvicorn(1 个进程)与 gunicorn 同步 worker(1 个 worker),视图里用 sleep 模拟等待,20 个并发请求由本机线程发出;各组合只测了一次
    • 没有验证:真实的数据库 / 网络等待、gunicorn 多 worker 或 gthread worker、并发数远大于 20 时的线程数上限、runserver / daphne 上的表现
    京ICP备2026067502号-1京公网安备11010502063487号