【Django】源码解析django启动和访问过程(二)

Django启动过程(二)

上一篇讲到了启动参数被交给了‘django.core.management.commands.runserver.Command.handle’进行处理,下面继续解读源码。

django.core.management.commands.runserver.Command.handle

    def handle(self, *args, **options):
    	# 非DEBUG模式,必须在settings中设置ALLOWED_HOSTS
        if not settings.DEBUG and not settings.ALLOWED_HOSTS:
            raise CommandError('You must set settings.ALLOWED_HOSTS if DEBUG is False.')
		# 判断socket是否支持ipv6
        self.use_ipv6 = options['use_ipv6']
        if self.use_ipv6 and not socket.has_ipv6:
            raise CommandError('Your Python does not support IPv6.')
        self._raw_ipv6 = False
        # 如果没有传入ip:port,设置默认的ip和port,默认ip后续还有设置
        if not options['addrport']:
            self.addr = ''
            self.port = self.default_port
        else:
       		# 端口判断
            m = re.match(naiveip_re, options['addrport'])
            if m is None:
                raise CommandError('"%s" is not a valid port number '
                                   'or address:port pair.' % options['addrport'])
			# 从传入参数中获取IP和端口及其他参数                                   
            self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups()
            # 端口不正确抛出异常
            if not self.port.isdigit():
                raise CommandError("%r is not a valid port number." % self.port)
            # ip的相关判断
            if self.addr:
                if _ipv6:
                    self.addr = self.addr[1:-1]
                    self.use_ipv6 = True
                    self._raw_ipv6 = True
                elif self.use_ipv6 and not _fqdn:
                    raise CommandError('"%s" is not a valid IPv6 address.' % self.addr)
        # ip为空的相关操作
        if not self.addr:
            self.addr = self.default_addr_ipv6 if self.use_ipv6 else self.default_addr
            self._raw_ipv6 = self.use_ipv6
        # 进行上述的参数拆分后,进入启动步骤
        # 这里调用自己的run方法
        self.run(**options)

参数进过校验过后交给了‘django.core.management.commands.runserver.Command.run’继续处理。

django.core.management.commands.runserver.Command.run

    def run(self, **options):
        """Run the server, using the autoreloader if needed."""
        # 代码变更是否自动重载
        use_reloader = options['use_reloader']
		# 自动重载启动,调用自己的inner_run方法
        if use_reloader:
            autoreload.run_with_reloader(self.inner_run, **options)
        else:
        	# 普通启动
            self.inner_run(None, **options)

调用‘django.core.management.commands.runserver.Command.inner_run’开始启动服务。

django.core.management.commands.runserver.Command.inner_run

    def inner_run(self, *args, **options):
    	...
        # 执行系统检查
        self.check(display_num_errors=True)
        # 执行数据库迁移检查
        self.check_migrations()
		...
		# 执行‘django.contrib.staticfiles.management.commands.runserver.get_handler’方法;
		# 该方法先调用‘django.core.management.commands.runserver.Command.get_handler’方法;
		# ‘get_handler’方法调用‘django.core.servers.basehttp.get_internal_wsgi_application’方法;
		# ‘get_internal_wsgi_application’获取到settings文件中设置的'WSGI_APPLICATION';
		# 如果没有设置则得到‘django.core.wsgi.get_wsgi_application’;
		# ‘get_wsgi_application’进行了django启动前的一系列操作,如加载setting配置,进行app注册和模块导入等;
		# 实际得到'django.core.handlers.wsgi.WSGIHandler'对象的实例,然后赋值给handler变量;
		# 如果是处理静态文件服务,且为DEBUG模式或者启动参数传入了‘--insecure’,则继续调用StaticFilesHandler对handler进行处理;
        handler = self.get_handler(*args, **options)
        # 执行‘django.core.servers.basehttp.run’方法,用WSGIServer启动服务
        run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
        ...

经过系统检查,数据库迁移检查,获取WSGIHandler实例对象后交由‘django.core.servers.basehttp.run’启动一个WSGIServer服务。

django.core.handlers.wsgi.WSGIHandler

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 调用父类的load_middleware加载中间件
        # 通过装饰器convert_exception_to_response修饰过的self._get_response作为初始handler,
        # 从settings.MIDDLEWARE中导入中间件,用handler去实例化中间件列表中最后一个中间件,
        # 并将实例化后的结果再次用convert_exception_to_response修饰以后赋值给handler,
        # 再如此往复用handler按照中间件列表从下往上的次序实例化剩下的中间件,
        # 这里实际上利用了python的闭包原理,将中间件的方法进行封包。
        # 实际上就是将中间件的self.get_response=self._get_response得到中间件实例mw_instance,
        # 把mw_instance件的‘process_view’方法按照‘settings.MIDDLEWARE’列表从上往下放入self._view_middleware
        # 把mw_instance的‘process_template_response’方法按照‘settings.MIDDLEWARE’列表从下往上放入self._template_response_middleware
        # 把mw_instance的‘process_exception’方法按照‘settings.MIDDLEWARE’列表从下往上放入self._exception_middleware
        # 中间件的执行顺序依次process_request->process_view->如果response有render方法执行process_template_response->过程中如果有错执行process_exception->process_response
        # convert_exception_to_response(mw_instance)封装中间件,赋值给self._middleware_chain
        # convert_exception_to_response是一个装饰器函数,定义了方法的执行和错误处理方式
        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 = [
            *response.items(),
            *(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
        ]
        start_response(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.servers.basehttp.run

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
	# 封装地址对象
    server_address = (addr, port)
    # 启用多线程,启动参数添加--nothreading则不启用多线程
    if threading:
    	# 用socketserver.ThreadingMixIn和WSGIServer定义启动类
    	# httpd_cls = class WSGIServer(socketserver.ThreadingMixIn,WSGIServer):...
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {
     })
    else:
        httpd_cls = server_cls
    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
    # WSGIHandler
    httpd.set_app(wsgi_handler)
    # 这里用socketserver.TCPServer.serve_forever()持续监听server_address
    # 当接收到请求时候调用WSGIHandler的__call__(),封装request和response
    httpd.serve_forever()

run方法启动了一个TCPServer服务,该服务将监听内容交由WSGIRequestHandler处理,
并将self.application设置为前面获取到的WSGIHandler实例。
到这里基本完成了django启动过程的源码解读,也从中了解了中间件的加载过程,下一篇将继续介绍django的请求过程。

你可能感兴趣的:(Django从入门到精通)