2. django运行过程分析
第一个过程分析:django启动过程
python mangage.py runserver 0.0.0.0:8000
这个命令先被python的sys.argv接收起来,保存成[ mangage.py, runserver, 0.0.0.0:8000]
然后execute_from_command_line(sys.argv)
它会调用ManagementUtility(argv).execute()
而execute()中,会先调用settings.INSTALLED_APPS,再调用django.setup()方法,然后调用self.fetch_command(subcommand).run_from_argv(self.argv)方法
*******先看settings.INSTALLED_APPS理论上就是访问settings.py中的INSTALLED配置
为什么?因为在运行manage.py时,第一个代码:os.environ.setdefault已经把配置文件设置到了DJANGO_SETTINGS_MODULE中,而这个DJANGO_SETTINGS_MODULE是保存在os.environ中的;然后就到这里的代码settings.INSTALLED_APPS,python导入settings后,才能使用settings.INSTALLED_APPS,导入settings时,会执行LazySettings(),而LazySettings中,重写了setattr方法,这意味着一旦我们访问LazySettings中的实例属性,那么就会自动执行setattr方法,又由于settings就是LazySettings(),也就是LazySettings的实例对象,所以settings.INSTALLED_APPS时,就会执行LazySettings()._setup()方法,这个方法中加载之前保存的DJANGO_SETTINGS_MODULE到LazySettings()中,也就是settings中,所以现在settings中就有了INSTALL_APPS,就能访问到了settings配置中的App字典)
然后再看apps.populate(settings.INSTALLED_APPS)中的apps.populate(...)
*******然后看django.setup()
django.setup()中,先配置日志模块configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
然后set_script_prefix设置前缀为:"/"
最后看apps.populate(settings.INSTALLED_APPS)
先看apps是什么,追踪源码可以很容易知道apps就是Apps的实例化对象
它主要的作用是把settings.INSTALLED_APPS中保存的字符串,用反射机制的加载成对应字符串匹配的类,也就是加载成app
然后把app保存到Apps类中的app_configs中,key是app的标签,value是app
然后把app保存到Apps类中的apps中
这样,django中的apps,就保存了当前django项目中的app信息后面就可以使用了,所以django.setup()主要做了三件事儿
1. 初始化日志配置
2. 设置settings.FORCE_SCRIPT_NAME的值为:"/"
3. 初始化app应用
*******然后分析:self.fetch_command(subcommand).run_from_argv(self.argv)
这里也要分成两个部分,self.fetch_command(subcommand)就是self.fetch_command('runserver')
它最终会返回一个runserver.Command()类
所以self.fetch_command(subcommand).run_from_argv(self.argv)就是调用runserver.Command().run_from_argv(self.argv)
那么runserver.Command().run_from_argv(self.argv)做了什么,核心看self.execute(*args, **cmd_options)
*******self.execute(*args, **cmd_options)
核心是:self.handle(*args, **options)
*******self.handle(*args, **options)
这里的self.handle要讨论下父子继承关系,直接用ctrl看self.handle的源码,发现没有什么可以看的,所以这里肯定用的是子类的handle方法,那么子类是谁,就是runserver.Command()类,所以我们直接看runserver.Command()类中的handler,
[图]
可以发现,这里又ipv6,port等等这些东西,这说明我们找对地方了,因为启动django服务器的本质就是启动web服务器,而web服务器的启动必须要有ip和端口的,因为之前我们讲过现在网络编程就是基于socket编程,所以底层一定又socket
再这里,self.handle(*args, **options)中,核心代码就是self.run(**options)
*******self.run(**options)
里面核心代码两条:
autoreload.run_with_reloader(self.inner_run, **options)
self.inner_run(None, **options)
其中autoreload.run_with_reloader主要是检查django的文件有没有变动,如果有变动则停止服务器,然后重启,重启时运行的代码还是self.inner_run,重启过程不是我们需要分析的重点,这里我们直接分析self.inner_run即可

*******self.inner_run(None, **options)
核心代码:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)

*******handler = self.get_handler(*args, **options)
由于目前的self还是Command,所以会调用Command().get_handler
Command().get_handler中,先调用了父类的get_handler,这个会根据settings配置来加载,最终得到WSGIHandler()
注意这里,它会导入wsgi.py文件,并执行里面的代码,会导致WSGIHandler()得到实例化,

然后有个if条件,满足if条件返回StaticFilesHandler(WSGIHandler())
不满足返回WSGIHandlers,这里我们讨论核心WSGIHandlers

********************************现在看下核心代码:WSGIHandler()

而WSGIHandler()实例化后,会调用WSGIHandler.load_middleware()方法,这个方法非常重要
它的源码如下:
self._view_middleware = []
self._template_response_middleware = []
self._exception_middleware = []

get_response = self._get_response_async if is_async else self._get_response
handler = convert_exception_to_response(get_response)
handler_is_async = is_async
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
middleware_can_sync = getattr(middleware, 'sync_capable', True)
middleware_can_async = getattr(middleware, 'async_capable', False)
if not middleware_can_sync and not middleware_can_async:
raise RuntimeError(
'Middleware %s must have at least one of '
'sync_capable/async_capable set to True.' % middleware_path
)
elif not handler_is_async and middleware_can_sync:
middleware_is_async = False
else:
middleware_is_async = middleware_can_async
try:
# Adapt handler, if needed.
adapted_handler = self.adapt_method_mode(
middleware_is_async, handler, handler_is_async,
debug=settings.DEBUG, name='middleware %s' % middleware_path,
)
mw_instance = middleware(adapted_handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if str(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
else:
handler = adapted_handler

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,
self.adapt_method_mode(is_async, mw_instance.process_view),
)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(
self.adapt_method_mode(is_async, mw_instance.process_template_response),
)
if hasattr(mw_instance, 'process_exception'):
# The exception-handling stack is still always synchronous for
# now, so adapt that way.
self._exception_middleware.append(
self.adapt_method_mode(False, mw_instance.process_exception),
)

handler = convert_exception_to_response(mw_instance)
handler_is_async = middleware_is_async

# Adapt the top of the stack, if needed.
handler = self.adapt_method_mode(is_async, handler, handler_is_async)
# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._middleware_chain = handler
先看第一条:get_response = self._get_response_async if is_async else self._get_response
这句代码中,先不看异步处理部分,那么get_response = self._get_response
而self._get_response中,有一句代码:
callback, callback_args, callback_kwargs = self.resolve_request(request)
里面的self.resolve_request(request),就是用来解析请求的,这个代码是Django进行路由控制的第一行代码
这里可以思考一下,这个代码现在就会开始解析URL吗?显然不会,因为get_response = self._get_response只是赋值,没有调用
第二条:handler = convert_exception_to_response(get_response)
这个代码里面就像一个装饰器,里面封装了异步执行get_response的代码;这里没有执行get_response
第三条:handler_is_async = is_async 设置异步执行状态,默认为False
for middleware_path in reversed(settings.MIDDLEWARE): 反向遍历中间件配置
middleware = import_string(middleware_path) 导入中间件
middleware_can_sync = getattr(middleware, 'sync_capable', True) 获取中间件是否可以同步执行的重要标志,如果没有设置,则True
middleware_can_async = getattr(middleware, 'async_capable', False) 同上,设置异步状态默认为False
然后一对条件,判断是异步异常还是同步执行,这里我们只看同步
接着创建adapted_handler,在同步模式下,这个代码会直接返回handler,也就是get_response,也就是self._get_response
adapted_handler = self.adapt_method_mode(
middleware_is_async, handler, handler_is_async,
debug=settings.DEBUG, name='middleware %s' % middleware_path,
) # 这里也没有执行get_response
mw_instance = middleware(adapted_handler) 在同步状态下,adapted_handler就是get_response,所以这里就是把adapted_handler传入middleware中,交给middleware去执行;每个中间件执行时,都会传入当前的get_response方法
在中间件中会根据get_response进行初始化,但是并不会立刻执行

接着就是判断中间件有没有属性'process_view'、'process_template_response'、'process_exception',如果有就将其添加到中间件的列表中,再执行 handler = convert_exception_to_response(mw_instance) 把中间件实例化也做成一个convert_exception_to_response的函数,这个handler会保存在for循环中,下一次循环的handler就会变成handler = convert_exception_to_response(mw_instance)
所以这会形成一个中间件的调用链,调用链的顶部恰好就是中间件配置文件的第一个
最后把这个调用链保存在self._middleware_chain中

******run(self.addr, int(self.port), handler, ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
这个代码是启动Django服务器的代码,相当于:
run(self.addr, int(self.port),WSGIHandlers, ipv6=self.use_ipv6, threading=threading, server_cls=WSGIServer)
其中的核心代码是:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
httpd.set_app(wsgi_handler)
httpd.serve_forever()
run的核心代码第二行的意思httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)
然后调用WSGIServer(...).set_app(WSGIHandler)
最后调用httpd.serve_forever()
我们来逐个解析,先解析httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)
这句话是做初始化,初始化WSGI服务器,里面封装了HTTPServer、TCPServe、BaseServer、sokect
初始化后,地址的绑定就不说了;重点是WSGIRequestHandler最终会赋值给BaseServer.RequestHandlerClass;
WSGIRequestHandler是什么东西等下再说
经过httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)的执行,httpd就成了一个实例化的WSGIServer
里面保存了WSGIRequestHandler这个非常重要的控制器

第二个是在WSGIServer中的setup_environ方法,这个方法虽然在初始化时不会调用,可是后面serve_forever后会被调用,主要的作用就是设置WSGIServer的环境变量env,并且这个参数会保存在self.base_environ中

然后再看WSGIServer(...).set_app(WSGIHandler)
它主要是把WSGIHandler保存在WSGIServer.application中

******httpd.serve_forever()
相当于WSGIServer.serve_forever(),它会调用BaseServer中的serve_foreve()
然后执行核心代码:self._handle_request_noblock()
******self._handle_request_noblock()
核心代码第一个:request, client_address = self.get_request()
这句话会让服务器阻塞在这里,并接收客户端传递过来的数据
所以到这里,django的启动就完成了

---------------------------------------------------接下来看django框架如何处理请求和响应数据---------------------------
假设我们使用浏览器客户端发送了HTTP请求过来了,那么就会被request接收,此时的请求还是一个socket对象
接着就会执行self.process_request(request, client_address),它的代码不多,直接贴出来:
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
这里会执行self.RequestHandlerClass(request, client_address, self)
之前讲过self.RequestHandlerClass就是WSGIRequestHandler,这是初始化时决定的
所以相当于执行WSGIRequestHandler(request, client_address),也就相当于初始化WSGIRequestHandler
初始化时,会通过父子继承关系,执行BaseRequestHandler.__init__的代码
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
主要把HTTP请求保存到了WSGIRequestHandler.request
把地址保存在WSGIRequestHandler.client_address
把WSGIServer保存在WSGIRequestHandler.server
然后调用WSGIRequestHandle.setup()方法
接着调用WSGIRequestHandle.handle()方法
最后调用WSGIRequestHandle.finish()方法
*******WSGIRequestHandle.setup()
根据继承关系,会调用到StreamRequestHandler.setup方法
def setup(self):
self.connection = self.request
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
if self.wbufsize == 0:
self.wfile = _SocketWriter(self.connection)
else:
self.wfile = self.connection.makefile('wb', self.wbufsize)
这个里面会把socket对象保存在WSGIRequestHandle.connection中
然后把WSGIRequestHandle.connection对象做成一个可读文件WSGIRequestHandle.rfiles,然后我们就可以像操作文件一样操作self.rfile了
然后再得到一个:self.wfil对象,它也是一个文件对象,可以实现写操作

********WSGIRequestHandle.handle()
def handle(self):
self.close_connection = True
self.handle_one_request()
while not self.close_connection:
self.handle_one_request()
try:
self.connection.shutdown(socket.SHUT_WR)
except (AttributeError, OSError):
pass
----这里会执行self.handle_one_request()
然后看核心self.handle_one_request()的核心代码:
self.raw_requestline = self.rfile.readline(65537) # 读取一行数据,一行数据最大65537
if not self.parse_request(): # 调用self.parse_request()方法,如果执行成功没事,执行失败则停止执行,停止后最终会进入下一个循环开始
接收下一个http请求
而self.parse_request()中,
* 先将self.raw_requestline的数据去掉换行符,然后保存再self.requestline中
接着切割requestline,判断单词的数量,如果有达到了3个,那么获取版本号并保存在self.request_version中
* 切割后把请求方法和路径都接收起来: command, path = words[:2]
并保存在self.command, self.path中
* 接着执行这行代码:self.headers = http.client.parse_headers(self.rfile,_class=self.MessageClass)
它的作用是读取请求头并把请求 头保存在self.headers中
也就是说,执行self.parse_request()后,self.handle_one_request()读取了请求行、请求头HTTP结构中的两大数据了,并把相关信息保存在了WSGIRequestHandle中
然后看self.handle_one_request()核心代码 handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ())
它生成了一个ServerHandler实例对象,这个对象中,保存了stdin,stdout,stderror,evn信息
然后把WSGIRequestHandler保存在ServerHandler.request_handler中(对应代码:handler.request_handler = self)
最后调用 handler.run(self.server.get_app()) 由于self.server是之前初始化时,保存的WSGIServer
相当于运行 ServerHandler.run(self.WSGIServer.get_app()) 由于WSGIServer.get_app()会返回self.application,
相当于运行 ServerHandler.run(WSGIServer.application) 而WSGIServer.application的值是之前设置的wsgi_handler,也就是WSIGHandler
相当于运行 ServerHandler.run(WSGIHandler())
所以handler.run(self.server.get_app()),相当于ServerHandler.run(WSGIHandler())
* handler.run(self.server.get_app())
里面的核心代码3行:
self.setup_environ()
self.result = application(self.environ, self.start_response)
self.finish_response()
先看第一行self.setup_environ(),相当于ServerHandler.setup_environ(),这个里面会把之前os.environ中的环境都copy一份过来
再看第二行self.result = application(self.environ, self.start_response),这个application是传入的WSGIHandler()
所以相当于运行WSGIHandler()(self.environ,self.start_response)
其中self.environ是上一行执行后保存的,它是从os.environ中copy的一份环境
然后self.start_response是一个方法名,这个方法中编写了处理响应数据的方法
而WSGIHandler()(self.environ,self.start_response),会执行WSGIHandler.__call__方法
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'):
# If `wsgi.file_wrapper` is used the WSGI server does not call
# .close on the response, but on the file wrapper. Patch it to use
# response.close instead which takes care of closing all files.
response.file_to_stream.close = response.close
response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size)
return response

在这个WSGIHandler的__call__方法中,
set_script_prefix(get_script_name(environ)) 是设置/作为路由的前缀
signals.request_started.send(sender=self.__class__, environ=environ) 这个不用管他,解决弱引用问题的
request = self.request_class(environ) # 相当于WSGIRequest(environ)
response = self.get_response(request) # 执行get_response方法,里面会调用self._middleware_chain(request),执行中间件
调用self._middleware_chain(request)后,就会解析URL了

Django 初步运行过程分析笔记的更多相关文章

  1. 《Hadoop技术内幕》读书笔记——Task运行过程分析

    本文是董西成的Hadoop技术内幕一书的读书章节总结. 第八章 Task运行过程分析 所有Task需要周期性地向TaskTracker汇报最新进度和计数器值,而这正是由Reporter组件实现的,其中 ...

  2. Java 学习笔记之 Thread运行过程分析

    Thread运行过程分析: 以下是一个最普通的Thread实现过程,我们今天就来看仔细分析下他是如何运行的. public class ThreadRunMain { public static vo ...

  3. Django Web开发指南笔记

    Django Web开发指南笔记 语句VS表达式 python代码由表达式和语句组成,由解释器负责执行. 主要区别:表达式是一个值,它的结果一定是一个python对象:如:12,1+2,int('12 ...

  4. Task的运行过程分析

    Task的运行过程分析 Task的运行通过Worker启动时生成的Executor实例进行, caseRegisteredExecutor(sparkProperties)=> logInfo( ...

  5. Django RF:学习笔记(8)——快速开始

    Django RF:学习笔记(8)——快速开始 安装配置 1.使用Pip安装Django REST Framework: pip install djangorestframework 2.在Sett ...

  6. django初步了解2

    目录 django初步了解2 表的字段增删改查 数据的增删改查 反向解析和分组 路由分发 名称空间 伪静态 虚拟环境 django初步了解2 表的字段增删改查 新增的字段 1.直接提供默认值 defa ...

  7. django初步了解

    目录 学前了解 wsgiref模块( web服务网关接口) 根据不同的功能拆封成不同的py文件 动静态网页 HTTP协议 django初步了解1 1.小白必会三板斧 2.静态文件配置 3.reques ...

  8. 全志V3S 编译运行xboot笔记

    目录 全志V3S 编译运行xboot笔记 1.目的 2.环境准备 3.下载 3.1 fel模式进入 3.2 sunxi-fel工具的使用 3.3 烧录 4.串口打印 5.总结 全志V3S 编译运行xb ...

  9. Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析,通过对LoadModel函数及 ...

  10. django开发项目实例1--建立一个项目并初步运行

    1:进入目标目录新建一个项目 D:\>django-admin.py startproject qiweijie 新建完成后,进入项目文件夹查看目录 D:\>cd qiweijie D:\ ...

随机推荐

  1. dp 优化

    dp 优化 \(\text{By DaiRuiChen007}\) I. [ARC085D] - NRE \(\text{Link}\) 思路分析 将最终的第 \(i\) 对 \(a_i\) 和 \( ...

  2. Java入门与进阶P-4.5+P-4.6

    逻辑类型 关系运算的结果是要给逻辑值,true或false.这个值可以保存在一个对应的逻辑类型变量中,这样的变量类型是boolean 布尔是为了纪念George Boole对逻辑计算得到贡献 bool ...

  3. Linux防火墙部署与配置

    Linux防火墙部署与配置 1. 实验概述 Linux作为网关,搭建小型局域网,在此基础上进行实验,了解Linux防火墙的构成.NAT和包过滤配置方法等. 2. 实验环境 网络大致结构如图2-1所示, ...

  4. 【学习笔记】C/C++ 设计模式 - 模板模式

    介绍说明 模板设计模式是一种非常简单的设计模式,其主要是利用了虚函数的特性实现.非常适合应用在一些算法.流程.业务逻辑是固定的形式,其中某些步骤的实现方式又无法确定下来的场景. 举例说明 以下为模拟某 ...

  5. 阿里云服务器中MySQL数据库被攻击

    前几天刚领了一个月的阿里云服务器玩,在里面装了MySQL,然后这几天找了个小项目练习着玩呢,就将表建在里面了. 刚访问添加员工还好好的,刚给员工分页查询呢 ,啪一下 ,很突然昂 ,就访问不了了 ,看控 ...

  6. 虚拟机配置代理(虚拟机nat)

    桥接 ​ 第一步:打开clash allow lan ​ 第二步:找到宿主机在局域网中的IP地址 ​ 第三步:配置虚拟机代理 NAT ​ 同上 注意 ​ 一.宿主机防火墙要配置好(直接关闭会也有效果, ...

  7. 一文看懂 Python 中的函数参数

    函数定义中的参数也就是形式参数,规定了在调用函数时如何传递实际参数以及这些参数有无默认值. 实参传递方式 def f(a): print(a) 实参传递方式有两种,位置和关键字.对于上面定义的函数 f ...

  8. 【Vue】计算属性 监听属性 组件通信 动态组件 插槽 vue-cli脚手架

    目录 昨日回顾 1 计算属性 插值语法+函数 使用计算属性 计算属性重写过滤案例 2 监听属性 3 组件介绍和定义 组件之间数据隔离 4 组件通信 父子通信之父传子 父子通信之子传父 ref属性 扩展 ...

  9. setInterval()的使用

    setInterval() 作用  这个函数可以将一个函数每隔一段时间执行一次 参数  1.回调函数,该函数会每隔一段时间被调用一次  2.每次调用间隔的时间,单位是毫秒 返回值  返回一个Numbe ...

  10. Cesium源码之flyTo(一)

    1 /** 2 * Flies the camera from its current position to a new position. 3 * 4 * @param {Object} opti ...