zoukankan      html  css  js  c++  java
  • flask 中的 werkzeug Local,LocalStack 和 LocalProxy 技术应用

    什么是 Local

    1. wsgi 每次请求,会把过程进行抽离无状态话,过程数据存储在本次请求的全局变量中,使用到了Local. Local 作为每次请求的全局命令空间,属于每次请求的私有
    2. LocalStack 与 Local 相似,在 Local 基础之上使用堆栈方式进行操作,管理
    3. LocalProxy 代理类,代理 Local 或 LocalStack 实例

    为什么使用 Local

      为什么使用自定义 Local,而不是 threading.local。这是由内核决定的

        1. web 应用在启动之后,是一单线+协成程启动的话,会污染全局变量,无法区分,

        2. 使用多线程+协成无法保证,派发请求的工作协程,无法保证同时工作时且分别位于多个线程内,彼此互不影响

    所以: werkzeug 给出了自己的解决方案:Local 和 LocalStack

    为什么使用 LocalProxy

      那么问题来了:请求的上下文的私有变量存储在 Local 和 LocalStack 中,那在多任务时,每次调用 from flask import request, g, session , 如何保证获取正确的上下文,而不发生混乱?

    在 flask.globals.py 中

    def _lookup_req_object(name):  
        top = _request_ctx_stack.top  
        if top is None:  
            raise RuntimeError('working outside of request context')  
        return getattr(top, name)  
    
    _request_ctx_stack = LocalStack()  
    request = LocalProxy(partial(_lookup_req_object, 'request'))  
    session = LocalProxy(partial(_lookup_req_object, 'session'))

    在 werkzeug.local.py 中, LocalProxy是一个 Local or LocalStack 的一个代理

    @implements_bool
    class LocalProxy(object):
     """"""
      __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")
    
      def __init__(self, local, name=None):
         object.__setattr__(self, "_LocalProxy__local", local)
          object.__setattr__(self, "__name__", name)
          if callable(local) and not hasattr(local, "__release_local__"):
            # "local" is a callable that is not an instance of Local or
             # LocalManager: mark it as a wrapped function.
             object.__setattr__(self, "__wrapped__", local)
    
       def _get_current_object(self):
          """Return the current object.  This is useful if you want the real
          object behind the proxy at a time for performance reasons or because
          you want to pass the object into a different context.
          """
          if not hasattr(self.__local, "__release_local__"):
              return self.__local()
          try:
              return getattr(self.__local, self.__name__)
          except AttributeError:
              raise RuntimeError("no object bound to %s" % self.__name__)
    
      def __getattr__(self, name):
          if name == "__members__":
              return dir(self._get_current_object())
          return getattr(self._get_current_object(), name)

    调用 reqeust:动态 request <= 动态的 _request_ctx_stack.top <= LocalStack() 每次调用产生使用新的实例与方法结合(request)<= LoaclStack.call?

    是的,每次调用 request,就会新产生一个proxy实例,每次pop, push, top 均是针对 Local 的操作,而 Local 的属性赋值与获取均是针对 get_ident 获取的!

    如:werkzeug.local.Local.py

    class Local(object):
        __slots__ = ("__storage__", "__ident_func__")
    
        def __init__(self):
            object.__setattr__(self, "__storage__", {})
            object.__setattr__(self, "__ident_func__", get_ident)
        """"""
        def __getattr__(self, name):
            try:
                return self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
    
        def __setattr__(self, name, value):
            ident = self.__ident_func__()
            storage = self.__storage__
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}

    perfect!每次新请求来临时,flask 会把上下文存储在 werkzeug Local 中,使用时根据线程或者协程id获取

    这样使用有什么好处

    1. 支持底层协程操作,提高扩展并发效率
    2. 避免整个应用对请求上下文的管理与传递
    3. 扩展兼容性perfect,实现了对第三方应用的插拔式扩展
    4. 可阅读性强,操作使用简单,易上手
  • 相关阅读:
    Android性能优化典范
    通过命令行连接oracle数据库/进入sql plus
    eclispe 出现超内存错误
    eclipse 重装了tomcat后配置路径
    dorado listener属性
    dorado问题查询&快捷键重命名
    dorado spring知识补充
    Eclipse导入包的快捷键
    dorado抽取js
    dorado中的creationType选择类型
  • 原文地址:https://www.cnblogs.com/spaceapp/p/12157940.html
Copyright © 2011-2022 走看看