2023年11月29日发(作者:)
Flask的上下⽂管理
庭院深深深⼏许,杨柳堆烟,帘幕⽆重数。⽟勒雕鞍游冶处,楼⾼不见章台路。
⾬横风狂三⽉暮,门掩黄昏,⽆计留春住。泪眼问花花不语,乱红飞过秋千去。
⼀、 通过Threading的local实现的本地存储隔离
当我们开启多线程来执⾏func函数,通过⾃定义的Foo类来存储数据时,我们发现最终的输出结果是全部的线程打印的都是⼀个最终的数字
10,这是因为这样存储的数据线程之间是共享的,当最后⼀个线程执⾏func函数时,由于func函数了1秒钟,第⼀个线程还没有来
得及打印数据呢,就被最后⼀个线程传的参数给覆盖了,从⽽线程们打印的结果都是相同的。
from threading import local
import threading
import time
foo = local()
def func(num):
= num
(1)
print(,t_thread().ident)
for i in range(1,11):
th = (target=func,args=(i,))
()
输出结果.png
⼆、实例化⼀个Flask应⽤
使⽤,可以实例化⼀个Flask应⽤。实例化的Flask应⽤有⼀些要点或特性需要注意⼀下:
app = Flask(__name__)
1. 对于请求和响应的处理,Flask使⽤库中的类和类。
werkzeugRequestResponse
2. 对于URL模式的处理,Flask应⽤使⽤库中的类和类,每⼀个URL模式对应⼀个实例,这些实例最终会作为参数
werkzeugMapRuleRuleRule
传递给类构造包含所有URL模式的⼀个“地图”。这个地图可以⽤来匹配请求中的URL信息。
Map
3. 当实例化⼀个Flask应⽤(这个应⽤的名字可以随便定义)之后,对于如何添加URL模式,Flask采取了⼀种更加优雅的模式,对于这点可
app
以和Django的做法进⾏⽐较。Flask采取装饰器的⽅法,将URL规则和视图函数结合在⼀起写,其中主要的函数是。这样写视图函数,
route
会将这条URL规则和视图函数联系起来,并且会形成⼀个实例,再添加进实例中去。当访问时,会执⾏。
'/'index()RuleMap'/'index()
4. 实例化Flask应⽤时,会创造⼀个环境,这是Flask⾃带的⼀种模板引擎。
Jinja
5. 实例化的Flask应⽤是⼀个可调⽤对象。在前⾯讲到,Web应⽤要遵循规范,就要实现⼀个函数或者⼀个可调⽤对象
WSGIwebapp(environ,
start_response)__call__(environ, start_response)
,以⽅便服务器或⽹关调⽤。Flask应⽤通过⽅法可以让它被服务器或⽹关调⽤。
注意到调⽤该⽅法会执⾏⽅法,之所以这样设计是为了在应⽤正式处理请求之前,可以加载⼀些“中间件”,以
wsgi_app(environ, start_response)
此改变Flask应⽤的相关特性。
6. Flask应⽤还有⼀些其他的属性或⽅法,⽤于整个请求和响应过程。
三、调⽤Flask应⽤时会发⽣什么
上⾯部分分析了实例化的Flask应⽤长什么样⼦。当⼀个完整的Flask应⽤实例化后,可以通过调⽤()⽅法运⾏这个应⽤。
Flask应⽤的run()⽅法会调⽤g模块中的run_simple⽅法。这个⽅法会创建⼀个本地的测试服务器,并且在这个服务器中运⾏
Flask应⽤。
当服务器开始调⽤Flask应⽤后,便会触发Flask应⽤的__(environ, start_response)⽅法。其中environ由服务器产⽣,start_response在
call
服务器中定义。
上⾯我们分析到当Flask应⽤被调⽤时会执⾏wsgi_app(environ, start_response)⽅法。可以看出,wsgi_app是真正被调⽤的WSGI应⽤,之所
以这样设计,就是为了在应⽤正式处理请求之前,wsgi_app可以被⼀些“中间件”装饰,以便先⾏处理⼀些操作。
四、上下⽂(application context 和 request context)
from functools import partial
from import LocalStack, LocalProxy
import copy
from functools import update_wrapper
from import ClosingIterator
from werkzeug._compat import PY2, implements_bool
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
// 数据保存在 __storage__ 中,后续访问都是对该属性的操作
object.__setattr__(self, '__storage__', {}) // {"__storage__":{}}
object.__setattr__(self, '__ident_func__', get_ident) // {"__storage__":{} , "__ident_func__" : get_ident}
def __iter__(self):
return iter(self.__storage__.items())
def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
LocalStack 是基于 Local 实现的栈结构。如果说 Local 提供了多线程或者多协程隔离的属性访问,那么 LocalStack 就提供了隔离的栈访问。
下⾯是它的实现代码,可以看到它提供了 push、pop 和 top ⽅法。
__ 可以⽤来清空当前线程或者协程的栈数据,__ ⽅法返回当前线程或者协程栈顶元素的代理对象。
release_localcall
class LocalStack(object):
def __init__(self):
self._local = Local() // {"__storage__":{},"__ident_func__":get_ident}
def __release_local__(self):
self._local.__release_local__()
def __call__(self):
def _lookup():
rv =
if rv is None:
raise RuntimeError('object unbound')
@implements_bool
class LocalProxy(object):
def __call__(self, environ, start_response):
return _app(environ, start_response)
def wsgi_app(self, environ, start_response):
ctx = t_context(environ)
app_ctx = _context()
)
每个 request context 都保存了当前请求的信息,⽐如 request 对象和 app 对象。在初始化的最后,还调⽤了 实现了路由的匹
match_request
配逻辑。


发布评论