bigywater.tech
Django · 24

WSGI 与 ASGI 启动流程对比

WSGI 和 ASGI 两个入口,启动流程有什么不同?同一个视图在两种入口下运行在哪个线程?这篇不启动服务器,直接导入 wsgi.py / asgi.py,并用最小的 WSGI 环境与 ASGI scope 调用 handler。

导入即启动
导入 wsgi.py / asgi.py 就会 django.setup() 并构建中间件链,与是否有请求无关
线程互换
WSGI:同步视图在主线程、async 视图进线程池;ASGI:同步视图进线程池、async 视图在主线程
lifespan
ASGIHandler 收到 lifespan 事件时抛 ValueError

导入时发生了什么

实测 · 本机执行输出
  WSGI:import proj.wsgi 之后 → django.core.handlers.wsgi.WSGIHandler | apps.ready = True | 事件: ['LabConfig.ready()', 'MW.__init__(中间件链在构建 handler 时创建)']
  ASGI:import proj.asgi 之后 → django.core.handlers.asgi.ASGIHandler | apps.ready = True | 事件: ['LabConfig.ready()', 'MW.__init__(中间件链在构建 handler 时创建)']

get_wsgi_application() 与 get_asgi_application() 都先 django.setup()(所以 ready() 在导入时就跑完),再创建 handler,handler 的构造函数里构建中间件链。

调用约定

实测 · 本机执行输出
  WSGIHandler.__call__ 签名  : (self, environ, start_response)
  ASGIHandler.__call__ 签名  : (self, scope, receive, send)  是协程函数: True

WSGI 是同步的可调用对象;ASGI 是协程函数,收发消息都通过 await。

视图在哪个线程里运行

实测 · 本机执行输出
  WSGI  同步视图 → ('200 OK', 'thread=MainThread main=True')
  WSGI  async 视图 → ('200 OK', 'async thread=ThreadPoolExecutor-1_0 main=False')
  ASGI  同步视图 → (200, 'thread=ThreadPoolExecutor-2_0 main=False')
  ASGI  async 视图 → (200, 'async thread=MainThread main=True')

这是理解「为什么在 ASGI 下调同步 ORM 会有线程问题」「为什么 WSGI 下的 async 视图没有好处」的关键:Django 在两种入口下都保证视图能运行,但「不匹配」的一方要付线程切换的代价(第 111–116 篇)。

lifespan

实测 · 本机执行输出
  ValueError: Django can only handle ASGI/HTTP connections, not lifespan.

需要 startup / shutdown 钩子时要自己包一层 ASGI 应用(本文没有做)。

分步演示:两个入口

WSGI vs ASGI
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。WSGI 用 wsgiref.util.setup_testing_defaults 构造 environ;ASGI 手写 scope / receive / send 并在 asyncio.run 里调用
    • 没有验证:真实 gunicorn / uvicorn / daphne 下的线程模型、lifespan 事件在 uvicorn 中的处理、线程池大小限制、thread_sensitive 的含义(第 113 篇)
    京ICP备2026067502号-1京公网安备11010502063487号