Django WSGI 源码解析:从协议到实现
本文详细解析 Django 基于 Python WSGI 标准的实现原理。从 WSGI 协议定义的 application 与 server 交互规范入手,结合 wsgiref 模块源码,梳理 make_server、WSGIServer 及 WSGIRequestHandler 的调用链。通过剖析请求处理生命周期,揭示 Django 内置服务器如何接收请求并调用应用逻辑,为深入阅读 Django 源码奠定基础。
前言
WSGI 协议概述
Python 作为脚本语言,在 Web 后端开发领域的应用日益广泛。与此同时,基于 Python 构建的 Web 应用程序框架层出不穷,例如 Bottle、Django 和 Flask 等。
当 HTTP 请求抵达服务器时,服务器负责接收请求并将其传递给 Web 应用程序进行解析。应用程序生成响应数据后返回给服务器,再由服务器反馈给客户端。这一过程涉及两个核心组件:服务器(Server)和应用程序(Application)。为了确保不同厂商开发的服务器和应用程序能够互操作,必须存在一种统一的契约规范。这类似于计算机通信早期,若各公司各自为政制定通信协议,市场将陷入混乱;因此,确立单一通用的通信协议至关重要。
针对 Python 语言的这一契约规范即为 WSGI(Python Web Server Gateway Interface)。其具体规范详见 PEP 333 文档。
在实习期间,笔者主要使用 Django 框架,以下是结合 Django 源码学习 WSGI 机制的笔记整理。
应用程序接口规范
应用程序(Application)
在应用程序端,必须提供符合 WSGI 规范的接口方法。
def simple_app(environ, start_response):
"""可能是最简单的处理了"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return ['Hello world!\n'] # 返回结果必须可迭代
除了直接提供函数外,还可以实现包含 __call__ 方法的类来充当应用程序。
该方法会被服务器调用。其中,environ 是一个字典对象,包含环境变量、REQUEST_METHOD、SCRIPT_NAME、QUERY_STRING 等请求信息;start_response 是一个回调函数,在应用程序内部被调用,用于启动 HTTP 响应。其原型大致如下。
def start_response(status, response_headers, exc_info=None):
...
return write # 返回这 write 函数 只是为了兼容之前的 web 框架, 新的框架根本用不到.
参数包括 status(状态码)、response_headers(HTTP 响应头,可修改)以及 exc_info(错误相关信息)。在生成响应数据过程中若发生错误,需通过再次调用 start_response 来更新 HTTP 头部。因此,更详尽的实现方式可能如下所示。
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
# do stuff w/exc_info here
finally:
exc_info = None # Avoid circular ref.
return write
服务器端实现
服务器(Server)
从服务器角度看,其最简工作模式是调用 simple_app() 函数,并将结果发送给客户端。
result = simple_app(environ, start_response) #名字不一定为 simple_app
try:
for data in result:
if data: # don't send headers until body appears
write(data)
if not headers_sent:
write('') # send headers now if body was empty
finally:
if hasattr(result, 'close'):
result.close()
需要注意的是,WSGI 并未详细规定 Web 应用程序和服务器内部的具体工作机制,仅定义了二者之间的连接标准。
Django 与 wsgiref 源码分析
Python wsgiref 模块
接下来观察 Django 如何实现 WSGI。Django 内部自带了一个用于本地测试的小型服务器,因此初学者无需搭建 Apache 或 Nginx 即可开始 Django 开发。Django 自带的服务器基于 Python 的 wsgiref 模块实现,其自带测试代码如下。
# demo_app() 是 application
def demo_app(environ,start_response):
from StringIO import StringIO
stdout = StringIO()
print >>stdout, "Hello world!"
print >>stdout
h = environ.items(); h.sort()
for k,v in h:
print >>stdout, k,'=', repr(v)
start_response("200 OK", [('Content-Type','text/plain')])
return [stdout.getvalue()]
def make_server(
host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server = server_class((host, port), handler_class)
server.set_app(app)
return server
if __name__ == '__main__':
httpd = make_server('', 8000, demo_app)
sa = httpd.socket.getsockname()
print "Serving HTTP on", sa[0], "port", sa[1], "..."
import webbrowser
webbrowser.open('http://localhost:8000/xyz?abc')
httpd.handle_request() # serve one request, then exit
Python 标准库提供了丰富的工具,这可能导致产生多个父类。为了清晰说明,根据 wsgiref 模块及其测试用例绘制了以下 UML 类图(注意:此图仅展示 wsgiref,未涉及 Django 内部逻辑)。

阅读这些代码时容易感到困惑,因为其继承关系较为复杂。因此,对测试代码的执行流程进行了简要概括。
- 在 make_server() 函数中,WSGIServer 类作为服务器类,负责接收请求、调用应用程序处理逻辑并返回响应。
- WSGIRequestHandler 作为请求处理类,已在 WSGIServer 中配置。
- 此外,还设置了 WSGIServer.application 属性(通过 set_app(app) 方法)。
- 最终返回 server 实例。
- 随后打开浏览器发起请求。服务器实例 WSGIServer httpd 调用自身的 handle_request() 函数处理请求。handle_request() 的工作流程如下:请求到达 -> WSGIServer 接收 -> 调用 WSGIServer.handle_request() -> 调用 _handle_request_noblock() -> 调用 process_request() -> 调用 finish_request() -> finish_request() 中实例化 WSGIRequestHandler -> 实例化过程中调用 handle() -> handle() 中实例化 ServerHandler -> 调用 ServerHandler.run() -> run() 调用 application() 执行真正逻辑 -> run() 中调用 ServerHandler.finish_response() 返回数据 -> 回到 process_request() 调用 WSGIServer.shutdown_request() 关闭请求(实际上该函数未执行具体操作)。
注:既然 application 是 WSGIServer 的属性,为何会在 ServerHandler 中调用?这是因为在实例化 WSGIRequestHandler 时,WSGIServer 将自己传递给了它。因此,在 WSGIRequestHandler 中实例化 ServerHandler 时,可以通过 WSGIRequestHandler.server.get_app() 获取到真正的 application。
总结与展望
总结
综上所述,启动服务器时,无论采用何种方式,都必须传递一个 application(),它可以是函数,也可以是实现了 __call__ 方法的类。当请求到达服务器时,服务器会自动调用 application() 以获取响应数据。至于如何具体处理请求数据并生成响应,则由 application() 内部逻辑细化决定。
确实,其中的调用链非常长,且上述过程尚未包含 HTTP 头的分析(如提取 Cookie 等)。如果仅需响应一个 "helloworld",完全可以在 WSGIServer.finish_request() 中直接返回数据,从而省略 WSGIRequestHandler 和 ServerHandler 类,只需提供一个 application() 即可。但事实上,Web 应用的响应远比 "helloworld" 复杂。
关于 Django 中 WSGI 的具体实现,将在下一节详细讨论。Django 源码剖析由此开始!笔者已在 GitHub 备份了 Django 源码的注释版:Decode-Django,感兴趣的读者可以 fork 查看。建议结合 python wsgiref、BaseHTTPServer.py 和 SocketServer.py 模块源码阅读本文效果更佳。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









