本文是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、该类实例必须可调用
最后
个人水平有限,不当之处还请指出,感激不尽。
另外,我也将上面的流程绘制成了流程图,需要的可在评论区留言。