Django框架的整个数据流向、服务启动、端口监听等基础核心功能都是按照WSGI标准进行开发的。WSGI是Web Server Gateway Interface的缩写,它的主要作用是接收客户端(浏览器/软件/APP/调试程序)发送的请求和转发请求给上层服务(例如Django)。它采用了select网络模型进行数据的接收和分发,可以利用操作系统的非堵塞和线程池等特性,因此它是非常高效的(Django采用wsgi是纯python代码实现,开源项目有一个uWSGI是C语言代码实现,因此现在更多的django项目实际上是运行在uWSGI上的,uWSGI不再讨论范围之内)。
接下来是记录我利用pycharm提供的断点调试功能以及自己打日志整个过程的梳理和总结。
源码的分析过程取决与分析人员想要关注的原理点,不同的需求就会有不同的侧重点,我当前的需求点是,想要知道django是如何通过网络层接收数据(wsgi)并将请求转发给django的urls层,因此我会朝着这个主题方向列出每个流转环节中的核心代码并做相应的注解。以后如果有机会的话我将会从另外一个侧重点(程序设计)去分析源码。
每个过程我都会先将代码列出来然后在后面做注解,因此告诫自己以后重读自己的笔记时不需要努力的去看代码,而是直接看注解,然后找到对应的代码进行理解。
1、准备工作
- Python 3.5.2
- Django 2.1.2
- PyCharm 2018.2.1 (Professional Edition)
- 启动项目
1 | [min:] ~/Desktop/python/Demo$ python manage.py runserver 0.0.0.0:8000 |
2、分析流程
manage.py
1 | #!/usr/bin/env python |
from django.core.management import execute_from_command_line
当这行代码开始执行时,首先会去运行django.core.management.__init__.py
这一整个文件,接着找到execute_from_command_line
函数并将其导入到当前程序的命名空间中;- 另外在导入之前,我们不可忽视的就是这个文件本身的import语句。
django/core/management/__init__.py
1 | from __future__ import unicode_literals |
import django
这行代码运行了django.__init__.py
文件。from django.apps import apps
这行代码运行了django.apps.__init__.py
文件,然而整个django的开端就是从这里开始的,它落实了非常多的事情(例如:初始化日志模块、加载INSTALL_APP、检查各APP是否正常、检查缓存模块是否正常等),当一切无误时才会往下走,否则将会报错退出程序。execute_from_command_line
这个方法是一个工厂函数,它负责指挥ManagementUtility类利用execute
方法来解析参数和启动wsgi服务。ManagementUtility.execute
方法中的一大堆if条件就是判断参数是否合法,重点还是在self.fetch_command(subcommand).run_from_argv(self.argv)
,这条命令应该拆成两部分去看。self.fetch_command
是利用django内置的命令管理工具去匹配到具体的模块,例如
self.fetch_command(subcommand)
其实就相当于是self.fetch_command('runserver')
,它最终找到了==django.contrib.staticfiles.management.commands.runserver.Command==这个命令工具。
django中的命令工具代码组织采用的是策略模式+接口模式,也就是说django.core.management.commands
这个目录下面存在各种命令工具,每个工具下面都有一个Command接口,当匹配到’runserver’时调用’runserver’命令工具的Command接口,当匹配到’migrate’时调用’migrate’命令工具的Command接口。run_from_argv(self.argv)
run_from_argv的作用是初始化中间件、启动服务,也就是拉起wgsi(但实际上并不是由它来直接完成,而是由后续很多其他代码来完成),直观上看它应该是runserver.Command对象的一个方法,但实际上要稍微更复杂一些,因为没有列出关联代码,所以在下一个代码块中进行说明。
小结
这部分代码实际上分为两部分,第一部分负责执行一些Django的基础服务,第二部分是一个匹配命令行参数的一个过程,比如本例中通过提供的参数’runserver’到命令工具集中去找到runserver模块。
django/contrib/staticfiles/management/commands/runserver.py
1 | from django.contrib.staticfiles.handlers import StaticFilesHandler |
- 当前类对象中不存在
run_from_argv
方法,因此我们要往下看它的继承对象django.core.management.commands.runserver.Command
。
django/core/management/commands/runserver.py
1 | from django.core.servers.basehttp import get_internal_wsgi_application, run |
- 当前这个类对象中也没有
run_from_argv
这个方法,因此我们要往下看它的继承对象django.core.management.base.BaseCommand
。
django/core/management/base.py
1 | class BaseCommand(object): |
由于接下来的代码重度使用了继承、多态、接口等设计模式的方式来运作,但是追溯起来并不复杂,所以就不列举出来了,这里重点强调一下
inner_run
中的self.get_handler(*args, **options)
方法,它非常重要,三个重点:- 因为它负责获取WSGIHandler;
- self.get_handler返回Django settings中
WSGI_APPLICATION
的值,一个WSGIHandler
对象; - 调用WSGIHandler的__init__方法,完成Django中重要的load_middleware操作。
Command.inner_run
方法调用了run
(即:django.core.servers.basehttp.run
),由于没有列出代码块,因此在下一个环节中进行说明。
小结
这部分代码实际上就是一个初始化过程,全部都为runserver服务,虽然很多代码我没有列出来,但是它确实做了一些,例如参数解析、端口指定检测、ipv4检测、ipv6检测、端口是否占用、线程检查,中间件加载等工作。
django/core/servers/basehttp.py
1 | from wsgiref import simple_server |
run
函数负责指挥各个对象负责启动wsgi服务。wsgi_handler
参数,这里传递的是WSGIHandler
;httpd_cls
这个变量被定义完成之后,由于大量的继承关系,它其实已经不单纯的属于django,它其实更像是一个传统意义上的WSGI服务对象了。httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
这行代码非常重要,因为它是WSGI服务器与django之间相互通信的唯一枢纽通道,也就是说,当WSGI服务对象收到socket请求后,会将这个请求传递给django的WSGIRequestHandler(下节会列出WSGIRequestHandler是如何工作的)。httpd.set_app(wsgi_handler)
是将WSGIHandler
传递给WSGIServer当作一个application,当WSGIServer收到网络请求后,可以将数据分发给django.core.servers.basehttp.WSGIRequestHandler
,最终由django.core.servers.basehttp.WSGIRequestHandler
将数据传递给application(即:django.core.handlers.wsgi.WSGIHandler
)。httpd.serve.forever()
启动非堵塞网络监听服务。
lib/python3.6/socketserver.py
1 | class BaseServer: |
上面我们看到最后一个动作是
httpd.serve_forever
,调用是socketserver.BaseServer.serve_forever
方法。该方法采用了selector网络模型进行等待数据,每0.5秒遍历一次文件描述符,当有数据进来时,ready变量会是一个socket请求对象,这时会将后续工作转交给self._handler_request_noblock
方法去处理,然后经过一系列的流转:_handler_request_noblock
process_request
finish_request
socketserver.BaseServer.RequestHandlerClass
RequestHandlerClass
是由httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
传递过来的参数django.core.servers.basehttp.WSGIRequestHandler
。 也就是说具体执行的是WSGIRequestHandler(request, client_address, self)
。
小结
serve_forever开启了一个while来无限监听网络层的scoket请求,当一条请求过来时,就层层转交到django.core.servers.basehttp.WSGIRequestHandler
手中。
django.core.servers.basehttp.py
1 | class WSGIRequestHandler(simple_server.WSGIRequestHandler, object): |
首先列举出来
django.core.servers.basehttp.WSGIRequestHandler
的继承分布,这个将对我们在分析源码的过程中起到一个非常重要的作用,方便我们理清楚各个类的作用:django.core.servers.basehttp.WSGIRequestHandler
wsgiref.simple_server.WSGIRequestHandler
http.server.BaseHTTPRequestHandler
socketserver.StreamRequestHandler
socketserver.BaseRequestHandler
object
因为
WSGIRequestHandler
并没有__init__
或者__call__
方法,因此需要遍历所有父类对象,最终在BaseRequestHandler
中看到了__init__
实例初始化方法,它调用了self.handle方法(即回调了:WSGIRequestHandler.handle
),具体见代码注释。ServerHandler
对象并没有run方法,最终在wsgiref.handlers.BaseHandler
中找到了run方法。
wsgiref.handlers.py
1 | class BaseHandler: |
application(self.environ, self.start_response)
也就相当于是WSGIHandler.__call__(self.environ, self.start_response)
。
django.core.handlers.wsgi.py
1 | class WSGIHandler(base.BaseHandler): |
- 通过层层流转,最终进入WSGIHandler,后续的动作我们会在接下来的章节继续介绍。
environ
这个变量在django的WSGIServer和WSGIRequestHandler中扮演这非常重要的角色,因为所有的客户端ip
、请求的URL
、cookie
、session
、header
等等信息都保存在其中。
3、总结
上面所有的过程都是django内部代码的为了启动服务而做的准备,简单的把流程给列出来。
- 解析运行 python manage.py 所提供的参数,例如: runserver;
- 根据参数 找到相对应的 命令管理工具;
- 加载所有的app;
- 检查端口、ipv4检测、ipv6检测、端口是否占用、线程检查、orm对象检查(表是否创建);
- 实例化WSGIRequestHandler,并且将它注册到python Lib库中的WSGIServer中;
- 最后启动python Lib库中的WSGIServer;
- WSGIServer处理socket请求、对接WSGIRequestHandler;
- WSGIRequestHandler针对
environ
进行预处理、对接ServerHandler; - ServerHandler最后调用了WSGIHandler;
- WSGIHandler用于执行应用程序(application)和返回响应给WSGIServer。