flask 中的 werkzeug Local,LocalStack 和 LocalProxy 技術應用

什麼是 Local

  1. wsgi 每次請求,會把過程進行抽離無狀態話,過程數據存儲在本次請求的全局變量中,使用到了Local. Local 做爲每次請求的全局命令空間,屬於每次請求的私有
  2. LocalStack 與 Local 類似,在 Local 基礎之上使用堆棧方式進行操做,管理
  3. LocalProxy 代理類,代理 Local 或 LocalStack 實例

爲何使用 Local

  爲何使用自定義 Local,而不是 threading.local。這是由內核決定的web

    1. web 應用在啓動以後,是一單線+協成程啓動的話,會污染全局變量,沒法區分,flask

    2. 使用多線程+協成沒法保證,派發請求的工做協程,沒法保證同時工做時且分別位於多個線程內,彼此互不影響session

因此: werkzeug 給出了本身的解決方案:Local 和 LocalStack多線程

爲何使用 LocalProxy

  那麼問題來了:請求的上下文的私有變量存儲在 Local 和 LocalStack 中,那在多任務時,每次調用 from flask import request, g, session , 如何保證獲取正確的上下文,而不發生混亂?併發

在 flask.globals.py 中app

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 的一個代理ide

@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?spa

是的,每次調用 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. 可閱讀性強,操做使用簡單,易上手
相關文章
相關標籤/搜索