Flask 源码剖析 :Flask 启动流程
Posted 懒编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask 源码剖析 :Flask 启动流程相关的知识,希望对你有一定的参考价值。
前言
Flask 是 Python 著名的 web 框架,其特点是轻量简单易扩展。
Flask 源码量挺多的,本文从比较高的维度整体看一下 Flask 关键结构的实现原理,文中不会细究太多细节,不多废话,开搞。
考虑篇幅长度,分多篇文章来讨论,本文系列文章以 Flask 1.0.2 为基准。
前置背景知识
Flask 依赖于 werkzeug 与 jinja 这两个核心库,werkzeug 是 HTTP 与 WSGI 相关的工具集,而 jinja 主要用于渲染前端模板文件。
Flask 框架满足 WSGI 协议,其功能简单而言就是将 HTTP 数据转为 environ 包含请求中所有的信息以及 start_response 回调函数传递给 web 框架对象,形象如图:
如果依旧不清晰,可以参考此前的旧文「实现满足 WSGI 协议的 Web 服务」。
Flask 应用启动流程
从 Flask 最基本的使用开始,代码如下。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "hello 懒编程"
if __name__ == '__main__':
app.run()
在代码中,通过 Flask(__name__)
实例化了 Flask 类,对应的__init__() 方法如下
# flask/app.py
classs Flask(_PackageBoundObject):
def __init__(...):
# 对每次请求,创建一个处理通道。
self.config = self.make_config()
self.view_functions = {}
self.error_handler_spec = {}
self.before_request_funcs = {}
self.before_first_request_funcs = []
self.after_request_funcs = {}
self.teardown_request_funcs = {}
self.teardown_appcontext_funcs = []
self.url_value_preprocessors = {}
self.url_default_functions = {}
self.url_map = Map()
self.blueprints = {}
self._blueprint_order = []
self.extensions = {}
__init__() 方法中有大量的注释,注释中解释了这些变量的用途,但仅从变量名就知道,它们用于存储每次请求对应的信息,相当于一个处理通道。
Flask 实例化后,接着利用 @app.route('/')
装饰器的方式将 hello () 方法映射成了路由,相关代码如下:
# flask/app.py/Flask
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
可以发现 route () 方法就是一个简单的装饰器,具体处理逻辑在 addurlrule () 方法中。
# flask/app.py/Flask
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None,
provide_automatic_options=None, **options):
# ... 省略其他代码细节
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
# 将路由rule添加到url_map中
self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
# 每个方法的endpoint必须不同
if old_func is not None and old_func != view_func:
raise AssertionError('View function mapping is overwriting an '
'existing endpoint function: %s' % endpoint)
# 将rule对应的endpoint与view_func通过view_functions字典对应上
self.view_functions[endpoint] = view_func
从 addurlrule () 方法可以看出, @app.route('/')
的主要作用就是将路由保存到 urlmap 中,将装饰的方法保存到 viewfunctions 中。需要注意的是,每个方法的 endpoint 必须不同,否则会抛出 AssertionError。
最后调用了 app.run () 方法运行 Flask 应用,对应代码如下。
# flask/app.py/Flask
def run(self, host=None, port=None, debug=None,
load_dotenv=True, **options):
# ... 省略
from werkzeug.serving import run_simple
try:
# 利用
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
run () 方法进一步调用 werkzeug.serving 下的 run_simple () 方法启动 web 服务,其中 self 就是 Flask () 的 application。
逐层深入,runsimple() -> makeserver() -> BaseWSGIServer() -> WSGIRequestHandler
WSGIRequestHandler 类从名称就可以知,它主要用于处理满足 WSGI 协议的请求,该类中的 execute () 方法部分代码如下。
# werkzeug/serving.py/WSGIRequestHandler
def execute(app):
application_iter = app(environ, start_response)
# 省略其他代码
简单而言,app.run () 会启动一个满足 WSGI 协议的 web 服务,它会监听指定的端口,将 HTTP 请求解析为 WSGI 格式的数据,然后将 environ, start_response 传递给 Flask () 实例对象。
类对象作为方法被调用,需要看到__call__() 方法,代码如需。
# flask/app.py/Flask
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
# 请求上下文
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
# 正确的请求处理路径,会通过路由找到对应的处理函数
# 通过路由找到相应的处理函数
response = self.full_dispatch_request()
except Exception as e:
# 错误处理
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 无论是否发生异常都需要从请求上下文中error pop出
ctx.auto_pop(error)
主要逻辑在 wsgiapp () 方法中,一开始进行了请求上下文的处理 (后面文章单独剖析 Flask 上下文相关源码),随后通过 fulldispatch_request () 方法找到当前请求路由对应的方法,调用该方法,获得返回,如果请求路由不存在,则进行错误处理,返回 500 错误。
fulldispatchrequest () 方法代码如下。
# flask/app.py/Flask
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
# 调用该路由对应的处理函数并将处理函数的结果返回。
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
fulldispatchrequest () 方法中最关键的逻辑在于 dispatch_request () 方法,该方法会将调用对应路由的处理函数并获得该函数的结果。
此外还有 trytriggerbeforefirstrequestfunctions()、preprocessrequest()、finalizerequest () 方法,这些方法会执行我们自定义的各种钩子函数,这些钩子函数会存储在 beforerequestfuncs()、beforefirstrequestfuncs()、afterrequestfuncs () 方法中。
最后,需要注意,app.run () 方法仅在开发环境中会被使用,通过上面的分析,已经知道 app.run () 背后就是使用 werkzeug 构建了一个简单的 web 服务,但这个 web 服务并不牢靠,生产环境通常利用 uWSGI,通过配置的形式,指定 WSGI app 所在文件来启动 Flask 应用。
结尾
本文主要讨论了 Flask 应用主要的运行流程,后面将会继续讨论 flask 的上下文、路由等机制。
如果你觉得本文对你有用,记得点「在看」支持二两。
以上是关于Flask 源码剖析 :Flask 启动流程的主要内容,如果未能解决你的问题,请参考以下文章