bigywater.tech
Flask · 7

请求进入 Flask 后的完整生命周期

一个请求从 WSGI 入口进来,到响应出去,Flask 内部按什么顺序做了哪些事?这篇用调用记录和源码把整条路径摆出来,再量一下钩子的成本。

wsgi_app
整个请求处理的入口函数
10
调用记录里出现的主要阶段函数数(request_context、push、full_dispatch_request 等)
0.55 µs
每个空 before_request 钩子的平均开销

一次请求经过的函数

实测 · 本机执行输出
    wsgi_app
      request_context
        create_url_adapter
      push
        app_context
          create_url_adapter
        push
        open_session
        match_request
      full_dispatch_request
        preprocess_request
          ensure_sync
          my_before
        dispatch_request
          ensure_sync
          index
        finalize_request
          make_response
          process_response
            ensure_sync
            my_after
            save_session
              should_set_cookie
      pop
        do_teardown_request
          ensure_sync
          my_teardown
        pop
          do_teardown_appcontext
  响应: 200 OK | X-After = 1 | Set-Cookie 存在: True

上一篇:Context Local 与线程隔离;下一篇:before_request、after_request、teardown_request。

WSGI 入口的源码

实测 · 本机执行输出
    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: BaseException | None = 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 "werkzeug.debug.preserve_context" in environ:
                environ["werkzeug.debug.preserve_context"](_cv_app.get())
                environ["werkzeug.debug.preserve_context"](_cv_request.get())
            if error is not None and self.should_ignore_error(error):
                error = None
            ctx.pop(error)

钩子的成本

实测 · 本机执行输出
     before_request 个数       每个请求(µs)
                     0           68.0
                    10           75.0
                   100          122.6
  每增加 1 个空钩子约 0.55 µs

分步演示

请求生命周期
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:真实网络服务器(gunicorn)下 wsgi_app 之前的 WSGI 服务器那一段;流式响应时 ctx.pop 的时机(第 42 篇);错误处理路径(handle_exception)在这篇没有展开,见第 8 与第 10 篇
    京ICP备2026067502号-1京公网安备11010502063487号