Django源码分析(二):runserver

1,444 阅读2分钟

起步

上节知悉了 django 是如何做到自动重启的之后,那么接下来了解下它是如何运行起一个 web 服务的。

开始

很熟悉的命令

通过下面的命令,我们便可将 django 跑起来。那么它发生了什么呢?

python3 manage.py runserver

简单的 web 服务

在此之前,根据 django 运行的服务,我们也利用 wsgiref 这个模块实现一个简单的 web 服务。


# server_demo.py

from wsgiref.simple_server import WSGIServer, WSGIRequestHandler

def demo_app(environ, start_response):
    from io import StringIO
    stdout = StringIO()
    print("Hello world!", file=stdout)
    print(file=stdout)
    h = sorted(environ.items())
    for k, v in h:
        print(k, '=', repr(v), file=stdout)
    start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
    return [stdout.getvalue().encode("utf-8")]


s = WSGIServer(("127.0.0.1", 1234), WSGIRequestHandler)
# 设置应用,注意了这里不一定非是个函数,可是一切可调用的对象
# 例如 函数、方法、类或实现了 __call__ 方法的实例
s.set_app(demo_app)
# 开始监听请求,这里应该是用的 poll 或者 selector 的网络模型。
s.serve_forever()

运行起来 server_demo.py 这个文件,然后通过浏览器访问 127.0.0.1:1234,会发现页面响应了一堆内容(不必关心展示的内容具体是什么)。

django 的服务

接下来观察源码。

django/core/servers/basehttp.py 下的 run 方法


def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        httpd.daemon_threads = True
    # 这里的 wsgi_handler 来自于
    # from django.core.handlers.wsgi import WSGIHandler
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

WSGIHandler

这是一个 wsgi 应用,它被定义在了 proj/wsgi.py 下的 application 属性。(application 对应刚刚上面代码中出现的 wsgi_handler)

application 这个属性对应的是个 get_wsgi_application 函数,该函数调用的过程中,针对 django.core.handlers.wsgi.WSGIHandler 这个类进行了实例化,在 __init__ 方法中,加载了 django 中间件。


# django.core.handlers.wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 载入中间件(注意:这里很关键。)
        self.load_middleware()

总结

至此,我们了解到了 django 的服务是从 django/core/servers/basehttp.py 下的 run 方法启动了一个 wsgi 服务。这个可调用的对象来自于 django.core.handlers.wsgi.WSGIHandler,这个类在实例化(__init__)的过程中,加载了中间件。

我们也更应该了解下 wsgi