zoukankan      html  css  js  c++  java
  • Django深度剖析

    启动过程

    通过命令行执行

    python manage.py runserver 127.0.0.1:8000
    

    启动Django服务

    manage.py模块获取到命令行参数manage.py runserver 127.0.0.1:8000,然后将该参数交由execute_from_command_line方法处理。

    # manage.py
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)
    

    该模块调用django.core.management.__init__包下的ManagementUtility类方法,并把参数传递给该类初始化,
    然后调用ManagementUtilityexecute开始一连串动作

    • 首先获取命令行第一个参数,也就是runserver,第0个参数是manage.py程序本身,同时把剩下的参数存入到options变量,
      options=['127.0.0.1:8000'].
    • 调用ManagementUtility autocomplete检查系统环境变量是否设置了DJANGO_AUTO_COMPLETE。接着再调用ManagementUtility
      fetch_command方法获取命令模块,该fetch_command 通过调用get_commands方法将django.core.management.commands目录下所有
      默认的命令模块获取,同时将settings里已注册的app目录INSTALLED_APPS下顺序反转,依次将app目录下对应的management/commands模块获取到。
      这些模块都可以通过执行python manage.py 模块名调用。由于django.contrib.staticfilesapp下已经重写了runserver这个模块,所以默认
      django.core.management.commands目录下的runserver模块会被后面查找到的模块覆盖掉.然后fetch_command再调用load_command_class
      方法加载django.contrib.staticfiles.runserver模块,并将该模块的Command类实例化返回。
    • 通过得到返回的runserver模块下的Command对象,继续调用对象的run_from_argv方法,将整个命令行参数传入该方法, run_from_argv
      方法是基类django.core.management.base.BaseCommand的方法,如果要自定义manage.py的命令行参数模块,都必须实现该方法,或者可以直接
      继承该基类,该方法默认会依次调用对象本身的create_parser,创建命令行参数解析器,然后继续调用execute执行一些系统检查,包括数据库连接同步,
      可以通过改变对象的requires_system_checks属性为False则不进行该检查操作,requires_migrations_checks控制是否检查数据迁移计划行为.
    • 做完应用检查后,再调用对象的handle方法,该方法由于对象本身没有重写,所有是继承父类django.core.management.commands.runserver.Command
      handle方法,在该父类模块runserver中,为了保持程序向后兼容,所以将BaseRunserverCommand = Command,handle方法首先检查配置文件
      是否为DEBUG模式,如果是,则检查是否配置了ALLOWED_HOSTS属性,然后检查命令行参数是否启用了ipv6,如果启用了,还得检查socket是否支持ipv6,
      然后检查监听端口,默认端口8000handle方法会继续调用对象的run方法,该方法会检查命令行参数是否包含--noreload选项,如果包含则
      不启用自动加载,由于命令行没有传入该选项,则使用默认行为,即:启用自动加载功能。
    默认的命令行选项参数值
    --ipv6  # 默认为False,不使用IPV6
    --nothreading # 默认值True,默认值为使用多线程,使用该选项后,则不使用线程,
    --noreload  # 默认值True  默认自动加载,使用该选项后,则不自动加载代码,用于代码文件有改动时会重新加载代码
    --nostatic # 默认值True  默认处理指定STATIC_URL配置的静态文件服务,使用该选项后,将不处理页面加载后的静态文件的URL请求
    --insecure  # 默认值False  不允许在非DEBUG模式提供处理静态文件的服务,使用该选项后,即使DEBUG模式,也处理静态文件的请求
    
    • 所以run方法会获取命令行的选项--nothreading值,默认没有使用该选项,所以值为True,即使用多线程。然后调用django.utils.autoreload.main
      方法,并把对象本身的inner_run方法及命令行参数传入main方法,通过平台选择后,由于我的环境非java平台,所以继续调用autoreload模块下的
      python_reloader方法,该方法首先判断系统环境中是否包含RUN_MAIN环境,且值为true,如果不包含,则使用通过该模块的 restart_with_reloader
      方法,先设置环境变量RUN_MAIN='true''然后执行 subprocess模块的call方法,重新运行命令行参数

    通过这样一执行,等于上面所执行的步骤都将重来一遍,唯一不同的是再次调用python_reloader方法时,将是新开启一个线程执行之前传入对象的inner_run方法及命令行参数传入

    这也就是为什么有人说django每次启动都执行了2次的原因,
    
    • 以上步骤走完后,终于要到我们的inner_run方法了,该方法才是启动web server的关键,调用该方法,会打印一些我们每次启动Django服务的看到的基本信息,
    Performing system checks...
    
    System check identified no issues (0 silenced).
    May 10, 2017 - 00:15:45
    Django version 1.11, using settings 'cloud.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
    
    

    inner_run内部再次调用该对象的check_migrations方法同步表结构迁移检查,检查完毕后则调用对象的get_handler方法,

    对象本身重写了get_handler方法,同时又调用了父类的get_handler的方法,通过调用父类的get_handler获取一个WSGIHandler对象实例。

    首先调用django.core.servers.basehttp.get_internal_wsgi_application,该方法通过获取settings配置的WSGI_APPLICATION属性,默认
    的属性都是以项目名+'.wsgi.application'拼接而成的字符串值,默认的django项目下都有一个wsgi模块,即项目名.wsgi这样的模块,
    get_internal_wsgi_application检测到该settings,再调用django.utils.module_loading.import_string方法,该方法首先以.分割字符串,
    WSGI_APPLICATION的值会分隔为项目名.wsgiapplication两部分,前半部分为模块路径,后半部分为模块内的属性,先导入该模块,再获取到
    该模块的属性,然后返回,默认得到的是一个WSGIHandler实例化对象,django.core.handlers.wsgi.WSGIHandler

    class WSGIHandler(base.BaseHandler):
        request_class = WSGIRequest
    
        def __init__(self, *args, **kwargs):
            super(WSGIHandler, self).__init__(*args, **kwargs)
            self.load_middleware()
    
        def __call__(self, environ, start_response):
            set_script_prefix(get_script_name(environ))
            signals.request_started.send(sender=self.__class__, environ=environ)
            request = self.request_class(environ)
            response = self.get_response(request)
            response._handler_class = self.__class__
    
            status = '%d %s' % (response.status_code, response.reason_phrase)
            response_headers = [(str(k), str(v)) for k, v in response.items()]
            for c in response.cookies.values():
                response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
            start_response(force_str(status), response_headers)
            if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
                response = environ['wsgi.file_wrapper'](response.file_to_stream)
            return response
    

    实例化时,同时会调用WSGIHandler父类django.core.handlers.base.BaseHandlerload_middleware方法。

    该方法首先判断Django使用的哪种版本,1.10(不包括1.10)以前的版本对应settings配置的中间件都是写到MIDDLEWARE_CLASSES列表里,
    1.10开始改为MIDDLEWARE,

    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
    
    def convert_exception_to_response(get_response):
        """
        包裹传入到response对象,所有的已知异常都会被转换成对应的4XX异常,如(Http404,
        PermissionDenied, MultiPartParserError, SuspiciousOperation),其它异常都会转换为500错误,
        这个装饰器自动应用到所有到中间件,确保中间件内部异常不会泄露而影响到后面到代码运行,
        然后下一个中间件可以继续处理上一个中间件返回的response,而不是抛出的异常
        """
        @wraps(get_response, assigned=available_attrs(get_response))
        def inner(request):
            try:
                response = get_response(request)  
            except Exception as exc:
                response = response_for_exception(request, exc)  # 对于中间件抛出的所有异常进行拦截,然后判断该响应是什么错误,
                                                                 # 已知的则返回对应的错误码,未知的,都是500错误
            return response
        return inner
    

    首先该django.core.handlers.base.BaseHandler._get_responseconvert_exception_to_response包裹起来,然后传入到中间件列表的
    最后中间件得到一个中间件实例对象,然后
    判断该实例对象是否包含了process_view方法,如果包含,则将该对象的process_view插入到一个_view_middleware的列表的第一个位置,
    判断该实例对象是否包含了process_template_response方法,如果包含,则将该对象的process_template_response方法追加到
    _template_response_middleware列表末尾,
    判读该实例对象是否包含了process_exception方法,如果包含,则将该对象的process_exception方法追加到_exception_middleware列表末尾,
    最后将该中间件实例通过convert_exception_to_response方法包裹,进行下一次迭代时传给下一个中间件作为实例化时的参数对象,直到所有的中间件
    都被实例化,然后将最后一个中间件的实例保存到
    django.core.handlers.base.BaseHandler对象的_middleware_chain属性里。

        def get_handler(self, *args, **options):
            """
            Returns the static files serving handler wrapping the default handler,
            if static files should be served. Otherwise just returns the default
            handler.
            """
            handler = super(Command, self).get_handler(*args, **options)
            use_static_handler = options['use_static_handler']
            insecure_serving = options['insecure_serving']
            if use_static_handler and (settings.DEBUG or insecure_serving):
                return StaticFilesHandler(handler)
            return handler
    

    整个WSGIHandler对象实例化完毕,同时返回给django.core.management.commands.runserver.get_handler,再由该方法返回给它的子类
    django.contrib.staticfiles.management.commands.runserver.get_handler方法。
    get_handler处理静态文件服务时,检查当前的程序模式是否为DEBUG或命令行是否包含--insecure选项,如果是则将WSGIHandler实例化对象
    传入给StaticFilesHandler类进行实例化,并返回,该类也继承了WSGIHandler.如果非DEBUG模式,且没有包含--insecure选项,则直接将之前的
    实例化对象返回给django.core.management.commands.runserver.Command.inner_run方法,

    def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
        server_address = (addr, port)
        if threading:
            httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, server_cls), {})
        else:
            httpd_cls = server_cls
        httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
        if threading:
            httpd.daemon_threads = True
        httpd.set_app(wsgi_handler)
        httpd.serve_forever()
    # 将命令行的参数解析后的值传入run方法,并将handler对象传入
    run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
    

    看到这个run方法是不是有种很熟悉的感觉,没错,它就是像我们平时使用socketserver方法一样的,被传入的handler会在有连接请求的时候,
    自动调用该handler类的handle方法处理请求,通过serve_forever就进入无限循环状态,直到强制终止,socket服务端才停止服务。


    到此,整个Django程序才算完成启动,剩下的都是socket server的操作方式了

  • 相关阅读:
    18软工实践-第三次作业-结对项目1
    结对作业之代码规范
    ALPHA(7)
    ALPHA(6)
    ALPHA(五)
    404 Note Found 现场编程
    ALPHA(四)
    ALPHA冲刺(三)
    ALpha冲刺(二)
    ALPHA 冲刺(一)
  • 原文地址:https://www.cnblogs.com/zengchunyun/p/6831716.html
Copyright © 2011-2022 走看看