Django WSGI 源码解析:从协议到实现

拾光
拾光
发布时间:2026-10-13 | 4人浏览过
原创

本文详细解析 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 模块源码阅读本文效果更佳。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表