本文是2023年技术复盘文章之一。
复盘带来思考,通过思考取其精华,精进自我,提升赚钱能力。
平时对
注:生产环境和开发环境,仅入口文件及前面的某些流程有别而已,所以就以开发服务器启动的流程来分析。另外,我制作了一张启动的流程图,如想获取,移步文末看获取的方法。
第一步:入口文件启动
在上一篇django是如何加载settings的复盘中已有描述,
def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
utility = ManagementUtility(argv)
utility.execute()
第二步 :载入配置文件、载入django 模块
这里不再赘述,可看上一篇文章django是如何加载settings的:
在载入配置文件后,会继续载入
class ManagementUtility:
# 省略其他代码
def exexute(self):
# 省略其他代码
django.setup()
# 省略其他代码
# 省略其他代码
对
只要运行了

当然,这个步骤还有不少琐碎之事,如:
- 解析命令行参数和选项
- 热重载时语法检查(闭包函数实现)
第三步:调用Command 类运行命令
需要先行注明的是,该环节有点绕,涉及到三个类,分别是:
1、当前
2、父类
3、祖父类
走入这个绕脑袋的流程,就从
class ManagementUtility:
# 省略其他代码
def exexute(self):
# 省略其他代码
django.setup()
# 省略其他代码
self.fetch_command(subcommand).run_from_argv(self.argv)
# 省略其他代码
1、跳转到祖父类中的
def run_from_argv(self, argv):
# 省略其他代码
try:
self.execute(*args, **cmd_options)
except CommandError as e:
if options.traceback:
raise
# 省略其他代码
2、当前类中并没有
def execute(self, *args, **options):
# 省略其他代码
# 一灯注:检查迁移文件
if self.requires_migrations_checks:
self.check_migrations()
# 一灯注:handle是个非常重要的节点的入口
output = self.handle(*args, **options)
# 一灯注:连接数据库
if output:
if self.output_transaction:
connection = connections[options.get("database", DEFAULT_DB_ALIAS)]
output = "%s
%s
%s" % (
self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
output,
self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
)
self.stdout.write(output)
return output
3、
def handle(self, *args, **options):
if not settings.DEBUG and not settings.ALLOWED_HOSTS:
raise CommandError("You must set settings.ALLOWED_HOSTS if DEBUG is False.")
# 省略其他代码
self.run(**options)
4、
def inner_run(self, *args, **options):
# 省略其他代码
# 一灯注:检查迁移文件,和上面的检查迁移文件是二选一,即总会有一个节点是要检查的
self.check_migrations()
try:
handler = self.get_handler(*args, **options)
# 一灯注 run函数入参,传入了handler
run(
self.addr,
int(self.port),
handler,
ipv6=self.use_ipv6,
threading=threading,
on_bind=self.on_bind,
server_cls=self.server_cls,
)
except OSError as e:
# 省略其他代码
pass
这里是一个重要的分水岭,在该代码中,
def get_handler(self, *args, **options):
"""
Return the static files serving handler wrapping the default handler,
if static files should be served. Otherwise return the default handler.
"""
handler = super().get_handler(*args, **options)
use_static_handler = options["use_static_handler"]
insecure_serving = options["insecure_serving"]
if use_static_handler and (settings.DEBUG or insecure_serving):
return StaticFilesHandler(handler)
return handler
再来说说,父类的
注:
1、
WSGIHandler 有__call__ 方法,上面的流程,获取到的都是它的类实例,这个类实例最后会交给底层服务器,由底层服务器调用实例,也就是调用__call__ 方法;2、它在实例化时,自动加载一次中间件,并把所有中间件可调用的方法组合成一个
中间件链(middleware_chain) ,供后面所使用。
再回头看看
第五步:运行run 函数
该方法在
def run(
addr,
port,
wsgi_handler,
ipv6=False,
threading=False,
on_bind=None,
server_cls=WSGIServer,
):
server_address = (addr, port)
if threading:
httpd_cls = type("WSGIServer", (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if on_bind is not None:
on_bind(getattr(httpd, "server_port", port))
if threading:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
这一步,我们只关注重点:
1、
2、
3、在底层中,如果有请求,就会调用
4、在
至此,整个启动流程就结束了。
题外话
- 加载中间件
- 控制请求逐一经过请求中间件
- 解析
url ,获取视图 - 请求经过视图,生成响应
- 控制响应逐一经过中间件
- 响应传递给底层服务器
这里就不展开了,内容真的很多。
总结
1、整个流程似乎没看到什么异步操作,毕竟上下文联系得很紧凑,完成一步才能走到下一步流程
2、
3、不过继承特性也有好处,至少在
4、会想起刚接触编程没多久,强行将各种不同功能函数揉成一个类,而各种教程也非常喜欢使用
一个类,旨在完成一项工作,一项工作需要很多细节,如果几项工作的细节雷同,那么可以抽象一个基类了,N项工作都能继承该基类,如果方式相同,但细节不同,也可以抽象基类,子类可实现各自的细节。
问题
在
1、可能底层服务要求入参的
2、该类实例必须可调用
最后
个人水平有限,不当之处还请指出,感激不尽。
另外,我也将上面的流程绘制成了流程图,需要的可在评论区留言。