接着上一篇,现在我们从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请求,并处理响应。