django启动入口源码分析
manage.py
是启动入口,在里面调用execute_from_command_line(sys.argv)
方法
def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
utility = ManagementUtility(argv)
utility.execute()
ManagementUtility
对象的execute()
方法
def execute(self):
try:
subcommand = self.argv[1]
except IndexError:
subcommand = 'help' # Display help if no arguments were given.
if subcommand == 'help':
...
elif subcommand == 'version' or self.argv[1:] == ['--version']:
...
elif self.argv[1:] in (['--help'], ['-h']):
...
else:
# 关键代码,传入的subcommand='runserver'时,进入else分支
# 调用fetch_command()返回command对象,再调用对象的run_from_argv方法
self.fetch_command(subcommand).run_from_argv(self.argv)
fetch_command()
方法
def fetch_command(self, subcommand):
...
# Get commands outside of try block to prevent swallowing exceptions
# commands是一个字典,key是subcommand,value是该subsommand对应的类所在的包的名称
# 比如{'runserver': 'django.contrib.staticfiles, ...}
commands = get_commands()
try:
# subcommand = "runserver"
# app_name = "django.contrib.staticfiles"
app_name = commands[subcommand]
except KeyError:
...
sys.exit(1)
if isinstance(app_name, BaseCommand):
klass = app_name
else:
# 关键代码
# 加载"django.contrib.staticfiles.management.commands.runserver"模块中的Command类,
# 并且实例化该类,klass实际上是一个Command类实例
klass = load_command_class(app_name, subcommand)
return klass
klass
是一个django.contrib.staticfiles.management.commands.runserver
模块中的Command
类的实例对象,他的父类实际上是django.core.management.commands.runserver
中的Command类。
run_from_argv()
方法是klass
对象的顶级父类中的方法。该方法中最终会调用django.core.management.commands.runserver
的Command类的handle()
方法。
def handle(self, *args, **options):
...
# 传入参数开启服务
self.run(**options)
def run(self, **options):
"""Run the server, using the autoreloader if needed."""
use_reloader = options['use_reloader']
# debug模式
if use_reloader:
autoreload.main(self.inner_run, None, options)
else:
# 非debug模式,开启服务
self.inner_run(None, **options)
def inner_run(self, *args, **options):
...
try:
# 1. 先实例化WSGIHandler,得到全局唯一的application对象,handler就是application
handler = self.get_handler(*args, **options)
# 2. 然后再开启服务server_cls是django自己重写的WSGIServer类,继承自wsgiref的WSGIServer,
# 重写的不是关键方法,就当做是wsgiref的WSGIServer即可
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
except socket.error as e:
...
os._exit(1)
except KeyboardInterrupt:
if shutdown_message:
self.stdout.write(shutdown_message)
sys.exit(0)
再看一下run()
方法,是一个独立的函数在django.core.servers.basehttp
模块中
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
# threading默认为True,
if threading:
# 动态创建一个支持多线程的WSGIServer类,父类为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:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
其中WSGIServer
是Django自己定义的一个类,继承自wsgiref
模块的simple_server.WSGIServer
类,继承自标准库http.server
模块的HTTPServer
类,继承自标准库socketserver
模块的TCPServer
类
WSGIRequestHandler
是Django自己定义的一个类,继承自wsgiref
模块的simple_server.WSGIRequestHandler
类,它重写了父类的handle()方法
这个类被作为参数传入到WSGIServer
中,等到请求到来时,会触发WSGIRequestHandler
类的实例化,在它实例化时会触发handle()
方法的调用。
注:每来一个请求都会实例化一个
WSGIRequestHandler
对象出来,然后这个对象来处理这个请求。
来看一下django自定义的WSGIRequestHandler
这个类
class WSGIRequestHandler(simple_server.WSGIRequestHandler):
protocol_version = 'HTTP/1.1'
...
def get_environ(self):
for k, v in self.headers.items():
if '_' in k:
del self.headers[k]
# 关键在于调用了父类的get_environ()方法,wsgiref模块中的WSGIRequestHandler类的get_eniron()方法构造了environ字典
return super().get_environ()
# handle()方法在WSGIRequestHandler的__init__()方法中被调用
def handle(self):
"""Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
# raw_requestline是客户端发来的请求行数据,读取请求行信息,其实这里读取的并不仅仅是请求行,而是
# 读取了65537字节的数据到rfile这个缓冲输入流中,在parse_request()方法中都会对这个raw_requestline
# 信息用"\r\n"来分割,取出第一行,也就是指取出请求行的信息。
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(414)
return
# parse_request()方法解析socket对象收到的http请求报文的请求行信息,赋值给自己的属性
if not self.parse_request(): # An error code has been sent, just exit
return
# 实例化ServerHandler,django的WSGIHandler中传入的start_response对象就是这个handler
handler = ServerHandler(
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
)
handler.request_handler = self # backpointer for logging
# 在run()方法是调用application(environ, start_reponse)的
handler.run(self.server.get_app())
ServerHandler
的父类是wsgiref模块的BaseHandler
,它的作用就是用来调用application(environ, start_response)
的,它在WSGIRequestHandler
的handle()
方法中被实例化,同时把WSGIRequestHandler
对象的实例属性rfile
,wfile
,environ
作为初始化参数传入,此时ServerHandler
对象也持有这三个属性。然后调用run(application)
来调用application
。
class BaseHandler:
...
def run(self, application):
"""调用application"""
try:
self.setup_environ()
# 调用application(),返回一个response对象,在django中是HTTPResponse类的实例对象,赋值给了实例属性result
self.result = application(self.environ, self.start_response)
# 处理response对象中的数据发送到客户端
self.finish_response()
except:
try:
self.handle_error()
except:
# If we get an error handling an error, just give up already!
self.close()
raise # ...and let the actual server figure it out.
def setup_environ(self):
"""Set up the environment for one request"""
env = self.environ = self.os_environ.copy()
self.add_cgi_vars()
# 给environ字典添加其他的key:value,这些字段是PEP333要求必须添加的
env['wsgi.input'] = self.get_stdin()
env['wsgi.errors'] = self.get_stderr()
env['wsgi.version'] = self.wsgi_version
env['wsgi.run_once'] = self.wsgi_run_once
env['wsgi.url_scheme'] = self.get_scheme()
env['wsgi.multithread'] = self.wsgi_multithread
env['wsgi.multiprocess'] = self.wsgi_multiprocess
...
def finish_response(self):
"""处理application返回的result,是一个可迭代的response对象"""
try:
if not self.result_is_file() or not self.sendfile():
# response是一个可迭代对象,实现了__iter__()方法,返回response中保存的数据内容,data是html内容,不包含请求头的内容
for data in self.result:
self.write(data)
self.finish_content()
finally:
self.close()
def start_response(self, status, headers,exc_info=None):
"""'start_response()' callable as specified by PEP 3333"""
...
self.status = status
# 这个方法最重要的作用是把响应头信息封装在headers中,作为自己的实例属性保存起来
self.headers = self.headers_class(headers)
...
return self.write
def write(self, data):
"""'write()' callable as specified by PEP 3333"""
...
if not self.status:
...
# headers_sent初始值为False,用于标记响应头是否发送
elif not self.headers_sent:
# Before the first output, send the stored headers
self.bytes_sent = len(data) # make sure we know content-length
# 1.先把响应状态行+响应头通过wfile.write()方法发送给客户端,wfile就是一个跟socket关联的输出IO流对象
self.send_headers()
else:
self.bytes_sent += len(data)
# XXX check Content-Length and truncate if too many bytes written?
# 2. 再把响应体data通过wfile.write()方法发送到客户端
self._write(data)
# 这个方法没用
self._flush()
总结:
WSGIRequestHandler
的作用有三点:
- 实例化时把
socket
对象分别封装成rfile
和wfile
对象,他们都是带缓冲的IO流对象,用于接受和发送数据给客户端。 - 提供一个
get_environ()
方法,用来构造environ
字典 - 实例化时调用自己重写的
handler()
方法,将BaseHandler
的子类实例化,同时传入rfile
,wfile
,environ
参数。
ServerHandler
的作用有三点:
- 实例化时,接受
WSGIRequestHandler
对象的rfile
,wfile
,environ
作为初始化参数 - 调用自己的
run(application)
来调用application(environ, start_response)
。 - 调用自己的
finish_response()
来处理application(environ, start_response)
返回响应对象response
,迭代response
对象中存放的数据发送给客户端。
实际上ServerHandler
对象write(data)
方法中进行了3次socket.sendall()
调用,分别是:
socket.sendall(status_line)
响应状态行socket.sendall(headers)
响应头socket.sendall(body)
响应体
WSGIRequestHandler
通过自己的handle()
方法与ServerHandler
对象产生了关联。而ServerHandler
对象在自己的run(application)
方法中调用application(environ, start_response)
,而application(environ, start_response)
又是django框架处理请求的入口,整个django框架处理请求的逻辑就在application(environ, start_response)
内部去实现了。
application是一个WSGIHandler
类的实例化对象,application(environ, start_response)
调用实际上是调用了该对象的__call__(environ, start_response)
方法。
class WSGIHandler(base.BaseHandler):
# WSGIRequest类是django自己提供的请求类,实例化之后就是request对象
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 加载中间件实例到内存中,此时中间件实例的各种方法已经被包裹在 _get_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)
# 实例化WSGIRequest,得到request对象,request对象中此时并没有session属性,而是在中间件加载后,
# 请求进入session中间件时,在session中间件的process_request()方法中动态添加的session属性
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 = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
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
application(environ, start_response)
返回的是一个HTTPResponse
类的实例对象response
。在上面的ServerHandler
的,run(appliction)
方法可以看到,response
被赋值给了ServerHandler
的实例属性result
,最后把这个response
中包含的数据发送给客户端。
start_response()
方法的作用,主要是将响应头信息headers封装在Header对象中,然后把这个Header对象作为start_response
方法的持有者,即ServerHandler
对象的一个属性,最后在发送数据到客户端时,调用ServerHandler
对象的write(data)
方法时,能直接取到自己的属性headers来发送响应头给客户端。
django启动入口源码分析的更多相关文章
- Django搭建及源码分析(三)---+uWSGI+nginx
每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...
- 涨姿势:Spring Boot 2.x 启动全过程源码分析
目录 SpringApplication 实例 run 方法运行过程 总结 上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入 ...
- worker启动executor源码分析-executor.clj
在"supervisor启动worker源码分析-worker.clj"一文中,我们详细讲解了worker是如何初始化的.主要通过调用mk-worker函数实现的.在启动worke ...
- Spring Boot 2.x 启动全过程源码分析
Spring Boot 2.x 启动全过程源码分析 SpringApplication 实例 run 方法运行过程 上面分析了 SpringApplication 实例对象构造方法初始化过程,下面继续 ...
- supervisor启动worker源码分析-worker.clj
supervisor通过调用sync-processes函数来启动worker,关于sync-processes函数的详细分析请参见"storm启动supervisor源码分析-superv ...
- Django之DRF源码分析(二)---数据校验部分
Django之DRF源码分析(二)---数据校验部分 is_valid() 源码 def is_valid(self, raise_exception=False): assert not hasat ...
- Spring Boot 2.x 启动全过程源码分析(上)入口类剖析
Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boot 为什么这么简便的奥秘. 本篇基于 Spring Boot 2.0.3 版本进 ...
- Django rest framework 源码分析 (1)----认证
一.基础 django 2.0官方文档 https://docs.djangoproject.com/en/2.0/ 安装 pip3 install djangorestframework 假如我们想 ...
- Django中间件部分源码分析
中间件源码分析 中间件简介 中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Django的输入和输出.每个中间件组件都负责做一些特定的 ...
随机推荐
- cocos2dx 2.2.3 xcode5.0,新mac项目错误
cocos2dx 2.2.3 xcode5.0,新建mac项目报错 Undefined symbols for architecture x86_64: "cocos2d::extens ...
- OpenGL(十四) 模板测试
启用模板测试时,OpenGL会在内存中开辟一块空间作为模板缓冲区,里边保存了每个像素的"模板值",模板测试的过程就是把每一个像素的模板值与一个设定的模板参考值进行比较,符合设定条件 ...
- 安装Eclipse完PyDev插件中没有出现
假设你是在Window7在环境搭建.请确保您使用以管理员身份运行Eclipse. PyDev插件安装后没有显示是由于PyDev的执行须要Java7,能够通过升级JDK的版本号来完毕,而且配置环境变量( ...
- SQL_DML简单的操作
***********************************************声明*************************************************** ...
- html5 模糊匹配搜索框
使用bootstrap3-typeahead.js 文件在这里 引用: <script type="text/javascript" src="@Url.Conte ...
- C++使用libcurl做HttpClient(业务观摩,用C++封装过程式代码,post和get的数据,最好url编码,否则+会变成空格)good
当使用C++做HTTP客户端时,目前通用的做法就是使用libcurl.其官方网站的地址是http://curl.haxx.se/,该网站主要提供了Curl和libcurl.Curl是命令行工具,用于完 ...
- IOS开发之iOS深浅拷贝
这里主要侧重于集合类的深浅拷贝,主要事因为工作的时候遇到这个问题. 有不足的地方欢迎指正 首先我们需要有这样的一个前提: [array addObject:obj]; 这样obj的引用计数会增加1,如 ...
- 2018-4-25-- 2.在sublime3里安装git插件并连接GitHub
1.配置全局参数 Git的主要配置包括用户名.邮箱的设置.以及生成SSH密钥公钥等. 首先运行一下的命令设置git提交代码时自己的用户信息. 2.在sublime3里使用时需要配置push.defau ...
- Oracle报错:不是单组分组函数
报错:不是单组分组函数 实例:select sum(HWJZ) ,rq from JcChargeInfo 原因: 1.如果程序中使用了分组函数,则有两种情况可以使用: 程序中存在group by, ...
- spring.net的简单使用(一)入门
Spring.net是一个非常强大的框架和工具,下面是百度百科对它的介绍. Spring.NET为建立企业级应用提供了一套轻量级的解决方案.通过Spring.NET,我们可以用统一且透明的方式来配置应 ...