flask 中的 werkzeug Local,LocalStack 和 LocalProxy 技術(shù)應(yīng)用
什么是 Local
- wsgi 每次請求,會(huì)把過程進(jìn)行抽離無狀態(tài)話,過程數(shù)據(jù)存儲在本次請求的全局變量中,使用到了Local. Local 作為每次請求的全局命令空間,屬于每次請求的私有
- LocalStack 與 Local 相似,在 Local 基礎(chǔ)之上使用堆棧方式進(jìn)行操作,管理
- LocalProxy 代理類,代理 Local 或 LocalStack 實(shí)例
為什么使用 Local
為什么使用自定義 Local,而不是 threading.local。這是由內(nèi)核決定的
1. web 應(yīng)用在啟動(dòng)之后,是一單線+協(xié)成程啟動(dòng)的話,會(huì)污染全局變量,無法區(qū)分,
2. 使用多線程+協(xié)成無法保證,派發(fā)請求的工作協(xié)程,無法保證同時(shí)工作時(shí)且分別位于多個(gè)線程內(nèi),彼此互不影響
所以: werkzeug 給出了自己的解決方案:Local 和 LocalStack
為什么使用 LocalProxy
那么問題來了:請求的上下文的私有變量存儲在 Local 和 LocalStack 中,那在多任務(wù)時(shí),每次調(diào)用 from flask import request, g, session , 如何保證獲取正確的上下文,而不發(fā)生混亂?
在 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是一個(gè) Local or LocalStack 的一個(gè)代理
@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)
調(diào)用 reqeust:動(dòng)態(tài) request <= 動(dòng)態(tài)的 _request_ctx_stack.top <= LocalStack() 每次調(diào)用產(chǎn)生使用新的實(shí)例與方法結(jié)合(request)<= LoaclStack.call?
是的,每次調(diào)用 request,就會(huì)新產(chǎn)生一個(gè)proxy實(shí)例,每次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!每次新請求來臨時(shí),flask 會(huì)把上下文存儲在 werkzeug Local 中,使用時(shí)根據(jù)線程或者協(xié)程id獲取
這樣使用有什么好處
- 支持底層協(xié)程操作,提高擴(kuò)展并發(fā)效率
- 避免整個(gè)應(yīng)用對請求上下文的管理與傳遞
- 擴(kuò)展兼容性perfect,實(shí)現(xiàn)了對第三方應(yīng)用的插拔式擴(kuò)展
- 可閱讀性強(qiáng),操作使用簡單,易上手

浙公網(wǎng)安備 33010602011771號