Flask 源码解析:整体流程梳理

  1. 创建一个 Flask 实例 app 并启动。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from flask import Flask


    app = Flask(__name__)

    @app.route('/index')
    def index():
    return "index"

    if __name__ == '__main__':
    app.run()
    1. urlview_func 通过 app.add_url_rule()@app.route() 的方式维护到 app 的 url_mapview_functions 属性中。

      • url_map 包含了路由逻辑,view_functions 存储了对应的逻辑函数,二者通过 endpoint 相关联。
    2. app.run() 中可以接受很多参数,其中

      • threadedprocesses,用于开启线程支持和进程支持。
  2. app.run() 最终调用了 wsgi 的 run_simple() 函数。

    run_simple 共有三个参数:

    • environ:为初步处理的 request 请求
    • start_response:为回调函数
    • Response:的实例化对象,也是具体的 request 入口,负责具体的逻辑,不同的框架其实是第三个参数不同。

    run_simple() 做了两件事:

    1. 一件:srv = make_server(),创建服务器。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      def make_server(host=None, port=None, app=None, threaded=False, processes=1,
      request_handler=None, passthrough_errors=False,
      ssl_context=None, fd=None):
      """
      if threaded and processes > 1:
      raise ValueError("cannot have a multithreaded and multi process server.")
      elif threaded:
      return ThreadedWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd)
      elif processes > 1:
      return ForkingWSGIServer(host, port, app, processes, request_handler, passthrough_errors, ssl_context, fd=fd)
      else:
      return BaseWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd)

      make_server() 中可知,Flask 提供了三种 server,每个 server 都实现了 process_request()

      • ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer),多线程

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        class ThreadingMixIn:
        def process_request_thread(self, request, client_address):
        try:
        self.finish_request(request, client_address)
        self.shutdown_request(request)
        except:
        self.handle_error(request, client_address)
        self.shutdown_request(request)

        def process_request(self, request, client_address):
        t = threading.Thread(target = self.process_request_thread, args = (request, client_address))
        t.daemon = self.daemon_threads
        t.start()
      • ForkingWSGIServer(ForkingMixIn, BaseWSGIServer),多进程

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        class ForkingMixIn:
        def process_request(self, request, client_address):
        """Fork a new subprocess to process the request."""
        pid = os.fork() # 创建一个进程,下面的代码会同时在父进程和子进程中执行。
        if pid:
        # Parent process
        if self.active_children is None:
        self.active_children = set()
        self.active_children.add(pid)
        self.close_request(request)
        return
        else:
        # Child process.
        # This must never return, hence os._exit()!
        status = 1
        try:
        self.finish_request(request, client_address)
        status = 0
        except Exception:
        self.handle_error(request, client_address)
        finally:
        try:
        self.shutdown_request(request)
        finally:
        os._exit(status)
      • BaseWSGIServer(HTTPServer),单进程单线程。默认情况下是 BaseWSGIServer

      想要配置多线程或者多进程,则需要设置 threaded 或 processes 这两个参数,而这两个参数是从 app.run() 中传递过来的 app.run(**options) ---> run_simple(threaded,processes) ---> make_server(threaded,processes)

    2. 一件:srv.server_forver(),一直在监听端口,等待 request 连接。

      serve_forever() 最终是调用的 BaseServer.serve_forever()BaseServer.serve_forever() 会使用 selector 模块监听相应的端口

      1
      2
      3
      4
      5
      6
      7
      class BaseServer:
      def serve_forever(self, poll_interval=0.5):
      while not self.__shutdown_request:
      ready = selector.select(poll_interval)
      if ready:
      # 接收到具体请求后 进行处理
      self._handle_request_noblock()

      当有请求到来时:_handle_request_noblock() 调用 process_request() 调用 finish_request() 实例化 RequestHandlerClass()

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      class BaseServer:
      def _handle_request_noblock(self):
      try:
      request, client_address = self.get_request()
      except OSError:
      return
      if self.verify_request(request, client_address):
      try:
      # 接收到具体请求后 进行处理
      self.process_request(request, client_address)
      except Exception:
      self.handle_error(request, client_address)
      self.shutdown_request(request)
      except:
      self.shutdown_request(request)
      raise
      else:
      self.shutdown_request(request)

      def process_request(self, request, client_address):
      # 接收到具体请求后 进行处理
      self.finish_request(request, client_address)
      self.shutdown_request(request)

      def finish_request(self, request, client_address):
      # 每次都实例化了 WSGIRequestHandler 进行请求处理
      # 注意第三个参数把当前的 server 实例传入
      self.RequestHandlerClass(request, client_address, self)

      RequestHandlerClass 其实是 WSGIRequestHandler,最终请求的处理是在 WSGIRequestHandler 实例化过程中处理的。最终会将 run_simple() 中的第三个参数(也就是app)加括号执行,并传入参数 (environ, start_response)

  3. 从上可知,当一个请求到来时,会调用 Flask.__call__(environ, start_response)__call__ 方法调用 wsgi_app()wsgi_app() 具体流程如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class Flask(_PackageBoundObject):

    def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)


    # 这个文件是 Flask 的整个执行流程的入口
    def wsgi_app(self, environ, start_response):
    """
    1、获取 environ 并再次封装。返回 request、session(此时为空)、app(其实就是 self)
    ctx.request = Request(environ)
    ctx.session = None
    ctx.app=app
    """
    ctx = self.request_context(environ) # 实际执行ctx = RequestContext(self, environ)
    error = None
    try:
    try:
    # 2、把 app_ctx,request_ctx 放到“某个神奇”的地方。
    # 对 session 的操作,执行SecureCookieSessionInterface.open_session(),去 cookie 中获取 session 的值,反序列化解密之后给ctx.session 重新赋值(默认 session 的值存在 cookie 中)。
    ctx.push()

    # 3、执行视图函数,去“某个神奇”的地方获取 session
    # 然后加密,序列化,写入cookie,然后返回
    response = self.full_dispatch_request()
    except Exception as e:
    error = e
    response = self.handle_exception(e)
    except:
    error = sys.exc_info()[1]
    raise
    # 4、此处调用 flask.Response.__call__ 方法
    return response(environ, start_response)
    finally:
    if self.should_ignore_error(error):
    error = None

    # 5、“某个神奇”的地方位置清空 (请求结束)
    ctx.auto_pop(error)
    1. 将 request 和 session 相关封装到 ctx = RequestContext 对象中,此时 session 为空,request 为二次封装;将 app 和 g 封装到 app_ctx = AppContext 对象中。

    2. 去 cookie 中获取 session 的值,反序列化解密之后给 ctx.session 重新赋值(默认情况下 session 的值存在 cookie 中);通过 LocalStack 对象将 ctx、app_ctx 封装到 Local 对象中。

    3. 根据 environ 路由到具体的 view_function 执行,并返回 response。

    4. 执行一些善后工作。

      1. view_function() 的返回转换为 flask.Response 对象。
      2. 对 session 进行加密,然后 dumps 编程字符串,保存到 cookie 中(默认情况下)。
      3. 以 WSGI 的方式(其实就是 flask.Response 对象)将 status、headers、response body 返回给 WSGI Server。
  4. 最后,WSGI Server 再将 WSGI 格式的数据转换为 HTTP 格式的数据返回给客户端


Flask 源码解析:整体流程梳理
https://flepeng.github.io/021-Python-32-框架-Flask-Flask-源码解析:整体流程梳理/
作者
Lepeng
发布于
2024年9月25日
许可协议