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

配逻辑。