Flask 启动流程

注意:为了阅读方便,删除了注释和不相干的部分。

应用启动的代码是 app.run() ,这个方法的代码如下:

  1. def run(self, host=None, port=None, debug=None, **options):
  2. """Runs the application on a local development server."""
  3. from werkzeug.serving import run_simple
  4. ...
  5. # 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数
  6. # 注意第三个参数传进去的是 self,也就是要执行的 web application
  7. try:
  8. run_simple(host, port, self, **options)
  9. finally:
  10. self._got_first_request = False

**
调用 werkzeugrun_simple。需要注意的是:run_simple 的第三个参数是 self,也就是我们创建的 Flask() app。因为 WSGI server 不是本文的重点,现在只需要知道它的功能就行:监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app 去执行处理的逻辑。

对应的执行逻辑在 werkzeug.serving:WSGIRequestHandlerrun_wsgi 中有这么一段代码:

  1. def execute(app):
  2. application_iter = app(environ, start_response)
  3. try:
  4. for data in application_iter:
  5. write(data)
  6. if not headers_sent:
  7. write(b'')
  8. finally:
  9. if hasattr(application_iter, 'close'):
  10. application_iter.close()
  11. application_iter = None

可以看到 application_iter = app(environ, start_response) 就是处理请求并返回结果的地方。

要调用 app 实例,需要通过 __call__ 方法将 Flask 对象变为可调用,以便 WSGI 服务器调用

  1. def __call__(self, environ, start_response):
  2. """The WSGI server calls the Flask application object as the
  3. WSGI application. This calls :meth:`wsgi_app` which can be
  4. wrapped to applying middleware."""
  5. return self.wsgi_app(environ, start_response)

实际的 WSGI 是通过 wsgi_app 函数实现的,通过 wsgi_app 转发 web 服务器的请求

  1. def wsgi_app(self, environ, start_response):
  2. # 创建请求上下文
  3. ctx = self.request_context(environ)
  4. error = None
  5. try:
  6. try:
  7. # 把请求上下文压栈
  8. ctx.push()
  9. # 正确的请求处理路径,会通过路由找到对应的处理函数
  10. response = self.full_dispatch_request()
  11. except Exception as e:
  12. # 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常
  13. error = e
  14. response = self.handle_exception(e)
  15. except: # noqa: B001
  16. error = sys.exc_info()[1]
  17. raise
  18. return response(environ, start_response)
  19. finally:
  20. if self.should_ignore_error(error):
  21. error = None
  22. # 不管处理是否发生异常,都需要把栈中的请求 pop 出来
  23. ctx.auto_pop(error)

根据 environ 生成请求上下文,请求上下文内部工作如同一个栈,栈顶是当前活动的请求。 处理请求前,先请求上下文压栈,请求处理完成后再移出栈。在出栈时,应用的 teardown_request() 函数也会被执行。

通过 full_dsipatch_request 函数分发请求并对请求进行预处理和后处理,同时捕获HTTP 异常并处理,full_dsipatch_request 的代码如下:

  1. def full_dispatch_request(self):
  2. """Dispatches the request and on top of that performs request
  3. pre and postprocessing as well as HTTP exception catching and
  4. error handling.
  5. """
  6. self.try_trigger_before_first_request_functions()
  7. try:
  8. request_started.send(self) # 发送请求进入信号
  9. rv = self.preprocess_request() # 预处理请求
  10. if rv is None:
  11. rv = self.dispatch_request() # 如果预处理未返回处理结果,则进一步处理请求,并返回处理结果
  12. except Exception as e:
  13. rv = self.handle_user_exception(e) # 处理异常
  14. return self.finalize_request(rv) # 生成 Response 对象,并后处理请求

最核心的内容是通过 dispatch_request 处理请求并返回结果(通过 finalize_request 转换成 Response 对象),加上请求的 hooks 处理和错误处理的内容。

通过 preprocess_requestfinalize_request 分别在处理请求之前和处理之后添加了很多 hooks 。这些 hooks 包括:

  • 第一次请求处理之前的 hook 函数,通过 before_first_request 定义
  • 每个请求处理之前的 hook 函数,通过 before_request 定义
  • 每个请求正常处理之后的 hook 函数,通过 after_request 定义
  • 不管请求是否异常都要执行的 teardown_request hook 函数

dispatch_request 要做的就是找到对应的处理函数,并返回调用的结果,也就是路由的过程
**

  1. def finalize_request(self, rv, from_error_handler=False):
  2. response = self.make_response(rv) # 生成 response 对象
  3. try:
  4. response = self.process_response(response) # 响应后处理
  5. request_finished.send(self, response=response) # 发送请求完成信号
  6. except Exception:
  7. if not from_error_handler:
  8. raise
  9. self.logger.exception(
  10. "Request finalizing failed with an error while handling an error"
  11. )
  12. return response

使用 Flask 类中的 make_response() 方法生成响应对象(我们从 flask 导入并在视图函数中生成响应对象的make_response是 helpers 模块中的make_response 函数),然后调用 process_response() 方法处理响应,

process_response() 代码如下:

  1. def process_response(self, response):
  2. ctx = _request_ctx_stack.top
  3. bp = ctx.request.blueprint
  4. funcs = ctx._after_request_functions
  5. if bp is not None and bp in self.after_request_funcs:
  6. funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
  7. if None in self.after_request_funcs:
  8. funcs = chain(funcs, reversed(self.after_request_funcs[None]))
  9. for handler in funcs:
  10. response = handler(response)
  11. if not self.session_interface.is_null_session(ctx.session):
  12. self.session_interface.save_session(self, ctx.session, response)
  13. return response

在把响应发送给 WSGI 服务器前执行所有使用after_request 钩子注册的函数,这里如果有蓝图的话需要增加蓝图的后处理函数。然后根据session对象来设置 cookie。

参考资料

flask 源码解析:应用启动流程