Django10命令runserver详解

接着上一篇,现在我们从runserver.Command()中的self.handle方法开始讲起。

handle方法经过参数获取和检查之后,进入run方法,该方法允许使用autoreload或者普通方式允许inner_run方法。

由于使用python开发后台服务程序的时候,每次修改代码之后都需要重启服务才能生效比较麻烦,所以Django有自己的自动加载模块功能(autoreload.py),是通过 subprocess 模式创建子进程,主进程作为守护进程,子进程中一个线程负责检测文件是否发生变化,如果发生变化则退出,主进程检查子进程的退出码(exist code)如果与约定的退出码一致,则重新启动一个子进程继续工作(该部分稍后单独理解)。

下面我们来看看inner_run方法:

 def inner_run(self, *args, **options):
        autoreload.raise_last_exception()

        threading = options['use_threading']
        shutdown_message = options.get('shutdown_message', '')
        quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'

        self.stdout.write("Performing system checks...\n\n")
        self.check(display_num_errors=True)
        self.check_migrations()
        now = datetime.now().strftime('%B %d, %Y - %X')
        if six.PY2:
            now = now.decode(get_system_encoding())
        self.stdout.write(now)
        self.stdout.write((
            "Django version %(version)s, using settings %(settings)r\n"
            "Starting development server at http://%(addr)s:%(port)s/\n"
            "Quit the server with %(quit_command)s.\n"
        ) % {
            "version": self.get_version(),
            "settings": settings.SETTINGS_MODULE,
            "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
            "port": self.port,
            "quit_command": quit_command,
        })

        try:
            handler = self.get_handler(*args, **options)
            run(self.addr, int(self.port), handler,
                ipv6=self.use_ipv6, threading=threading)
        except socket.error as e:
            # Use helpful error messages instead of ugly tracebacks.
            ERRORS = {
                errno.EACCES: "You don't have permission to access that port.",
                errno.EADDRINUSE: "That port is already in use.",
                errno.EADDRNOTAVAIL: "That IP address can't be assigned to.",
            }
            try:
                error_text = ERRORS[e.errno]
            except KeyError:
                error_text = force_text(e)
            self.stderr.write("Error: %s" % error_text)
            # Need to use an OS exit because sys.exit doesn't work in a thread
            os._exit(1)
        except KeyboardInterrupt:
            if shutdown_message:
                self.stdout.write(shutdown_message)
            sys.exit(0)
在组织好打印信息后,使用self.get_handler(*args, **options)方法获取处理器,该方法调用get_internal_wsgi_application方法:

def get_internal_wsgi_application():
    from django.conf import settings
    app_path = getattr(settings, 'WSGI_APPLICATION')
    if app_path is None:
        return get_wsgi_application()

    try:
        return import_string(app_path)
    except ImportError as e:
        msg = (
            "WSGI application '%(app_path)s' could not be loaded; "
            "Error importing module: '%(exception)s'" % ({
                'app_path': app_path,
                'exception': e,
            })
        )
        six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
                    sys.exc_info()[2])
使用用户配置的WSGI_APPLICATION(project_demo.wsgi.application)或者获取默认的模块,即调用get_wsgi_application方法,该方法在使用django.setup(set_prefix=False)检查相关配置完好之后,返回WSGIHandler()。再看看WSGIHandler类,他继承了base.BaseHandler类,有一个属性request_class = WSGIRequest(稍后解释)

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super(WSGIHandler, self).__init__(*args, **kwargs)
        self.load_middleware()

他在初始化时调用了BaseHandler中的初始化方法以及load_middleware方法:

class BaseHandler(object):

   def __init__(self):
        self._request_middleware = None
        self._view_middleware = None
        self._template_response_middleware = None
        self._response_middleware = None
        self._exception_middleware = None
        self._middleware_chain = None

    def load_middleware(self):
        self._request_middleware = []
        self._view_middleware = []
        self._template_response_middleware = []
        self._response_middleware = []
        self._exception_middleware = []

        if settings.MIDDLEWARE is None:
            warnings.warn(
                "Old-style middleware using settings.MIDDLEWARE_CLASSES is "
                "deprecated. Update your middleware and use settings.MIDDLEWARE "
                "instead.", RemovedInDjango20Warning
            )
            handler = convert_exception_to_response(self._legacy_get_response)
            for middleware_path in settings.MIDDLEWARE_CLASSES:
                mw_class = import_string(middleware_path)
                try:
                    mw_instance = mw_class()
                except MiddlewareNotUsed as exc:
                    if settings.DEBUG:
                        if six.text_type(exc):
                            logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                        else:
                            logger.debug('MiddlewareNotUsed: %r', middleware_path)
                    continue

                if hasattr(mw_instance, 'process_request'):
                    self._request_middleware.append(mw_instance.process_request)
                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.append(mw_instance.process_view)
                if hasattr(mw_instance, 'process_template_response'):
                    self._template_response_middleware.insert(0, mw_instance.process_template_response)
                if hasattr(mw_instance, 'process_response'):
                    self._response_middleware.insert(0, mw_instance.process_response)
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.insert(0, mw_instance.process_exception)
        else:
            handler = convert_exception_to_response(self._get_response)
            for middleware_path in reversed(settings.MIDDLEWARE):
                middleware = import_string(middleware_path)
                try:
                    mw_instance = middleware(handler)
                except MiddlewareNotUsed as exc:
                    if settings.DEBUG:
                        if six.text_type(exc):
                            logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                        else:
                            logger.debug('MiddlewareNotUsed: %r', middleware_path)
                    continue

                if mw_instance is None:
                    raise ImproperlyConfigured(
                        'Middleware factory %s returned None.' % middleware_path
                    )

                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.insert(0, mw_instance.process_view)
                if hasattr(mw_instance, 'process_template_response'):
                    self._template_response_middleware.append(mw_instance.process_template_response)
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.append(mw_instance.process_exception)

                handler = convert_exception_to_response(mw_instance)
        self._middleware_chain = handler

重点理解一下load_middleware方法,这很重要。通常我们settings.MIDDLEWARE不为None,执行 handler = convert_exception_to_response(self._get_response),其中convert_exception_to_response方法是接受response(由self._get_response返回response),检查该response是否有异常。观察发现convert_exception_to_response的调用方式是convert_exception_to_response(get_response)(request),self._get_response的调用方式是self._get_response(request),所以此处是将self._get_response方法入口给convert_exception_to_response,同时将convert_exception_to_response方法入口给handler。下面来看看self._get_response实现过程:

def _get_response(self, request):
        """
        Resolve and call the view, then apply view, exception, and
        template_response middleware. This method is everything that happens
        inside the request/response middleware.
        """
        response = None

        if hasattr(request, 'urlconf'):
            urlconf = request.urlconf
            set_urlconf(urlconf)
            resolver = get_resolver(urlconf)
        else:
            resolver = get_resolver()

        resolver_match = resolver.resolve(request.path_info)
        callback, callback_args, callback_kwargs = resolver_match
        request.resolver_match = resolver_match

        # Apply view middleware
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)
            if response:
                break

        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            try:
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)

        # Complain if the view returned None (a common error).
        if response is None:
            if isinstance(callback, types.FunctionType):    # FBV
                view_name = callback.__name__
            else:                                           # CBV
                view_name = callback.__class__.__name__ + '.__call__'

            raise ValueError(
                "The view %s.%s didn't return an HttpResponse object. It "
                "returned None instead." % (callback.__module__, view_name)
            )

        # If the response supports deferred rendering, apply template
        # response middleware and then render the response
        elif hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                # Complain if the template response middleware returned None (a common error).
                if response is None:
                    raise ValueError(
                        "%s.process_template_response didn't return an "
                        "HttpResponse object. It returned None instead."
                        % (middleware_method.__self__.__class__.__name__)
                    )

            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)

        return response

他接受request参数,然后调用get_resolver方法,通过用户配置的settings.ROOT_URLCONF,调用RegexURLResolver(r'^/', urlconf)初始化一个url解析器(可单独研究url解析过程),然后借助request.path_info中的url信息,通过解析器获得对应的调用方法、返回参数等,并将获取信息的入口赋给request.resolver_match。然后循环执行self._view_middleware中存入的与view相关的中间件(中间件的载入过程会在后面说到),传入相关参数执行其process_view方法,这些方法没有response返回时(检查没有产生错误),将通过url解析获得的用户定义方法进行原子化(self.make_view_atomic(callback)),然后执行,获得response(此时若没有response则是错误的),然后循环使用_template_response_middleware中的process_template_response方法检查这个返回是否正确。

接下来回到load_middleware方法,循环便利reversed(settings.MIDDLEWARE)(对用户的输入进行翻转,说明该项检查的顺序是按照用户输入的顺序来的),先载入对应的中间件入口,已handler(convert_exception_to_response(self._get_response))为参数初始化该中间件,然后检查该中间件是否包含process_view / process_template_response / process_exception方法,并分别加入_view_middleware / _template_response_middleware / _exception_middleware中,最后将该中间件
放入convert_exception_to_response中并再次赋值给handler。循环进行之后,得到的handler就是每个中间件的循环嵌套。最后将配置好的handler赋值给self._middleware_chain。
现在来看看WSGIRequest这个类,他继承了http.HttpRequest,初始化了WSGI以及HTTP相关的request信息。
初始化完成inner_run方法中的handler之后,使用run方法启动服务器

def run(addr, port, wsgi_handler, ipv6=False, threading=False):
    server_address = (addr, port)
    if threading:
        httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
    else:
        httpd_cls = WSGIServer
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        # ThreadingMixIn.daemon_threads indicates how threads will behave on an
        # abrupt shutdown; like quitting the server by the user or restarting
        # by the auto-reloader. True means the server will not wait for thread
        # termination before it quits. This will make auto-reloader faster
        # and will prevent the need to kill the server manually if a thread
        # isn't terminating correctly.
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

该函数传入了地址、端口、处理器相关信息。当用户的HTTP请求到达服务器时,WSGIServer会创建WSGIRequestHandler实例,使用其handler方法来处理HTTP请求(其实最终是调用wsgiref.handlers.BaseHandler中的run方法处理)。WSGIServer通过set_app方法设置一个可调用(callable)的对象作为application,上面提到的handler方法最终会调用设置的application处理request,并返回response。application是一个可调用对象 (callable object),它接受 environ 和 start_response 两个参数,并返回一个字符串迭代对象 。可调用对象 包括 函数 、 方法 、 类 或者 具有 __call__ 方法的 实例 ; environ 是一个字典对象,包括CGI风格的环境变量(CGI-style environment variables)和 WSGI必需的变量(WSGI-required variables); start_response 是一个可调用对象,它接受两个常规参数 (status,response_headers)和 一个 默认参数 (exc_info); 字符串迭代对象 可以是字符串列表 、 生成器函数 或者 具有 __iter__ 方法的可迭代实例。

最后调用WSGIServer.serve_forever方法,启动一个永久的服务器,接受http请求,并处理响应。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值