• 07 flask源码剖析之用户请求过来流程


    07 Flask源码之:用户请求过来流程

    1.创建ctx = RequestContext对象

    • RequestContext对象封装Request对象

    • RequestContext对象封装session数据

    • 源码实现:

      def wsgi_app(self, environ, start_response):
          """
              ctx = RequestContext(self, environ)
              """
          # 2.1 创建RequestContext对象
          ctx = self.request_context(environ)
      
      def request_context(self, environ):
          return RequestContext(self, environ)
      
      request_class = Request
      
      class RequestContext(object):
          def __init__(self, app, environ, request=None, session=None):
              self.app = app
              if request is None:
                  """
                  request_class = Request
                  """
                  request = app.request_class(environ)
              self.request = request
              self.session = session
      

    2. 创建app_ctx = AppContext对象

    • AppContext对象封装App对象

    • AppContext对象封装g

    • 源码实现:

      def wsgi_app(self, environ, start_response):
          """
              ctx = RequestContext(self, environ)
              """
          # 2.1 创建RequestContext对象
          ctx = self.request_context(environ)
          error = None
          try:
              try:
                  """
                  2.2 执行ctx.push
                      - app_ctx = 创建AppContext对象(app,g)
                      - 将app_ctx放入local中
                      - 将ctx放入到local中
                      - session赋值
                      - 路由匹配
                   """
                  ctx.push()
      
          def push(self):
              top = _request_ctx_stack.top
              if top is not None and top.preserved:
                  top.pop(top._preserved_exc)
              app_ctx = _app_ctx_stack.top
              if app_ctx is None or app_ctx.app != self.app:
                  """
                  app_ctx = AppContext(app)
                  """
                  # 创建appcontext对象
                  app_ctx = self.app.app_context()
      
      def app_context(self):
          return AppContext(self)
      
      class AppContext(object):
          def __init__(self, app):
              self.app = app
              self.g = app.app_ctx_globals_class()
      

    3. 将ctx对象、app_ctx对象放到local中

    • 然后ctx.push触发将 ctx对象,通过自己的LocalStack对象将其放入到Local中

    • 然后app_ctx.push触发将 app_ctx对象,通过自己的LocalStack对象将其放入到Local中

    • Local的本质是以线程ID为key,以{“stack”:[]}为value的字典

      存储结构:{
      1111:{“stack”:[ctx,]}
      };

      ​ {
      ​ 1111:{“stack”:[app_ctx,]}
      ​ }

    • 源码示例:

          def push(self):
              top = _request_ctx_stack.top
              if top is not None and top.preserved:
                  top.pop(top._preserved_exc)
              app_ctx = _app_ctx_stack.top
              if app_ctx is None or app_ctx.app != self.app:
                  """
                  app_ctx = AppContext(app)
                  """
                  # 创建appcontext对象
                  app_ctx = self.app.app_context()
                  # push将app_ctx放入到local中
                  app_ctx.push()
                  self._implicit_app_ctx_stack.append(app_ctx)
              else:
                  self._implicit_app_ctx_stack.append(None)
      
              if hasattr(sys, "exc_clear"):
                  sys.exc_clear()
              """
              self = ctx = RequestContext(self, environ)
              """
              # push将ctx放入到local中
              _request_ctx_stack.push(self)
      
              if self.session is None:
                  session_interface = self.app.session_interface
                  self.session = session_interface.open_session(self.app, self.request)
      
                  if self.session is None:
                      self.session = session_interface.make_null_session(self.app)
      
              if self.url_adapter is not None:
                  # 路由匹配,将匹配到的endpoint放到request.url_rule中
                  self.match_request()
      

    4. 执行所有before_request函数以及所有的视图函数

    1. 执行full_dispatch_request函数
    2. 执行所有的before_request函数
    3. 执行视图函数
    • 源码示例:

      def wsgi_app(self, environ, start_response):
          """
              ctx = RequestContext(self, environ)
          """
          #2.1 创建RequestContext对象
          ctx = self.request_context(environ)
          error = None
          try:
              try:
                  # 做了很多事
                  """
                      2.2 执行ctx.push
                          - app_ctx = 创建AppContext对象(app,g)
                          - 将app_ctx放入local中
                          - 将ctx放入到local中
                          - session赋值
                          - 路由匹配
                   """
                  ctx.push()
                  # 2.3 执行before_request/视图/after_request (处理session)
                  response = self.full_dispatch_request()
      
      def full_dispatch_request(self):
          # 触发所有的before_first_request_funcs函数
          # 只在启动程序后,第一个请求到来时执行
          self.try_trigger_before_first_request_functions()
          try:
              # 信号,暂留
              request_started.send(self)
              # 执行before_request_funcs函数,如果有返回值就不执行视图函数了
              rv = self.preprocess_request()
              if rv is None:
                  # 执行视图函数
                  rv = self.dispatch_request()
                  except Exception as e:
                      rv = self.handle_user_exception(e)
                      # 视图函数执行之后
                      #  1.执行所有的after_request
                      #  2.保存session
                      return self.finalize_request(rv)
      

    5. 执行所有after_request函数

    • session会加密返回给用户浏览器放到cookie中

    • 源码示例:

      def finalize_request(self, rv, from_error_handler=False):
          # 将rv视图函数返回值,封装到Reponse对象中
          response = self.make_response(rv)
          response = self.process_response(response)
          return response
      
      response_class = Response
      
      def make_response(self, rv):
          if not isinstance(rv, self.response_class):
              if isinstance(rv, (text_type, bytes, bytearray)):
                  rv = self.response_class(rv, status=status, headers=headers)
                  return rv
      
      def process_response(self, response):
          ctx = _request_ctx_stack.top
          funcs = ctx._after_request_functions
          # 执行所有的after_request_funcs
          funcs = chain(funcs, reversed(self.after_request_funcs[None]))
          for handler in funcs:
              response = handler(response)
              if not self.session_interface.is_null_session(ctx.session):
                  # 保存session
                  self.session_interface.save_session(self, ctx.session, response)
                  return response
      

    6. 销毁ctx和app_ctx

    • 如果请求结束不销毁ctx和app_ctx的话,会造成内存泄漏

    • 分别调用ctx和app_ctx的pop方法

    • 源码示例:

      def wsgi_app(self, environ, start_response):
          """
              ctx = RequestContext(self, environ)
          """
          #2.1 创建RequestContext对象
          ctx = self.request_context(environ)
          error = None
          try:
              try:
                  """
                      2.2 执行ctx.push
                          - app_ctx = 创建AppContext对象(app,g)
                          - 将app_ctx放入local中
                          - 将ctx放入到local中
                          - session赋值
                          - 路由匹配
                   """
                  ctx.push()
                  # 2.3 执行before_request/视图/after_request (处理session)
                  response = self.full_dispatch_request()
                  except Exception as e:
                      error = e
                      response = self.handle_exception(e)
                      except:  # noqa: B001
                          error = sys.exc_info()[1]
                          raise
                          return response(environ, start_response)
                      finally:
                          if self.should_ignore_error(error):
                              error = None
                              # 2.4 销毁ctx/app_ctx
                              ctx.auto_pop(error)
      
      
      def auto_pop(self, exc):
          self.pop(exc)
      
      def pop(self, exc=_sentinel):
          app_ctx = self._implicit_app_ctx_stack.pop()
          finally:
              rv = _request_ctx_stack.pop()
              if app_ctx is not None:
                  app_ctx.pop(exc)
      
  • 相关阅读:
    centos7安装KVM
    keepalived高可用
    Jenkins-Pipeline 流水线发布部署项目
    centos7部署jenkins
    版本控制gitlab
    c语言寻找3000以内亲密数对
    c语言寻找1000以内的完全数
    c语言分解因式
    c语言判断给定日期是当年的第几天
    c语言计算程序运行时间
  • 原文地址:https://www.cnblogs.com/liubing8/p/11930727.html
Copyright © 2020-2023  润新知