本文试图回答三个问题:
1. “Web 应用”到底在做什么?
2. 为什么 Python 需要 WSGI?
3. WSGI 具体长什么样?我们如何利用它?
一、Web 应用的本质
1. HTTP 请求–响应模型
• 浏览器把 HTTP 报文(URL、Method、Header、Body)发给一台服务器。
• 服务器返回新的 HTTP 报文(Status-Line、Header、Body)。
• “Web 应用”负责生成这个响应报文的内容。
• 整个过程是 无状态 的:一次请求完成后,TCP 连接通常就被关闭。
2. 静态 VS 动态
• 静态:Nginx/Apache 直接把磁盘上的 PNG、CSS 原封不动传给客户端。
• 动态:需要程序根据请求参数/数据库内容现算出 HTML/JSON,再返回。
这就是我们写 Flask、Django、Rails、Express 的原因。
3. 三个角色
a) Web Server(Nginx、Apache、Gunicorn、uWSGI…)
b) Web Application(Flask/Django/PHP/Node…)
c) 协议 & 接口(FastCGI、Servlet、Rack、WSGI…)
本质:让 Server 能调用 Application,并把 HTTP 信息在两者之间正确搬运。
二、为什么要有 WSGI
1. 历史:一团乱麻
早期 Python Web 框架各搞各的(CGI、FastCGI、mod_python、Zope…),
服务器和框架强耦合,无法互换:
┌────────┐ mod_python ┌──────────┐
│ Apache │ ───────────> │ Django │
└────────┘ └──────────┘
想把 Apache 换成 Nginx?抱歉,不支持。
2. 需求:定义一个“最小公共接口”
• 只传递 HTTP 所需最基本的信息;
• 不依赖任何框架、服务器实现;
• 简单到可以用几个函数就实现;
• 留出中间件的空间。
3. 结果:PEP 333 / PEP 3333 — Web Server Gateway Interface (WSGI) 2003–2010,成为 Python Web 事实标准。
从此:
- Gunicorn、uWSGI、Waitress 等 WSGI Server 负责 Socket + Protocol
- Flask、Django、FastAPI、Bottle 等 WSGI Application 负责 业务逻辑
二者可以自由组合。
三、WSGI 协议长什么样
1. 规则(最小可行):
• Web 应用必须是 可调用对象 (callable):函数、方法、类实例皆可。
• 可调用签名:`application(environ, start_response) -> iterable`
- `environ`:一个 dict,装满了 CGI 风格的键值对。
例如:`REQUEST_METHOD`, `PATH_INFO`, `QUERY_STRING`, 以及所有 HTTP_ 前缀的 Header。
- `start_response(status, headers)`:回调函数,由 Server 提供。
• status: `"200 OK"`
• headers: `[("Content-Type","text/html")]`
- 返回值:一个可迭代对象(通常是 list/tuple 或 generator),
其元素必须是 bytes,Server 将把它们逐块写到 TCP 连接。
2. 最小 demo
# hello.py
def application(environ, start_response):
msg = b"Hello, WSGI!"
start_response("200 OK", [("Content-Type", "text/plain"),
("Content-Length", str(len(msg)))])
return [msg]
3. 不依赖任何第三方 Server 也能跑
# server.py
from wsgiref.simple_server import make_server
from hello import application
httpd = make_server("0.0.0.0", 8000, application)
print("Serving on http://127.0.0.1:8000")
httpd.serve_forever()
4. Flask/Django 只是包装
Flask 对外暴露的 `app = Flask(__name__)` 本质上就是 `application`。
gunicorn -w 4 "myapp:create_app()"
│
▼
Gunicorn master ===> 4 worker ===> Flask wsgi_app()
• Gunicorn 做多进程/线程,负责 Socket。
• 调用 Flask 的 `wsgi_app(environ,start_response)`。
• Flask 内部再创建 Request/Context、路由匹配、模板渲染。
• 得到 Response,Gunicorn 写回客户端。
四、WSGI 中间件
链式调用示意:
Server ──► Middleware A ──► Middleware B ──► Application
◄──────────────────────────────────────────────◄
典型用途:
- 统一日志/Trace ID
- 压缩 GZip
- 静态文件代理
- 统一错误页
Gunicorn、Werkzeug、Rollbar/Sentry 均提供现成 WSGI 中间件。
五、WSGI 的局限 & 未来
1. 阻塞:单条请求执行期间线程被占用,无法原生异步。
2. WebSocket:不是基于 HTTP 请求-响应模型,WSGI 无法胜任。
3. 解决方案:ASGI(Asynchronous Server Gateway Interface, PEP 484)。
• 支持 async/await、WebSocket、HTTP2。
• 对标 FastAPI / Starlette / Django 3+ async。
• 思想与 WSGI 相同,只是把调用协议改为异步。
六、总结
Web 应用的本质:
“Server 负责 连接,框架负责 内容,中间靠 协议接口 说话。”
WSGI 关键 3 件事:
1. `environ` 输入字典
2. `start_response()` 回调
3. `bytes iterable` 输出