爲何使用自定義 Local,而不是 threading.local。這是由內核決定的web
1. web 應用在啓動以後,是一單線+協成程啓動的話,會污染全局變量,沒法區分,flask
2. 使用多線程+協成沒法保證,派發請求的工做協程,沒法保證同時工做時且分別位於多個線程內,彼此互不影響session
因此: werkzeug 給出了本身的解決方案:Local 和 LocalStack多線程
那麼問題來了:請求的上下文的私有變量存儲在 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 獲取的!線程
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獲取