flask中间件和LOCAL对象
中间件
我们知道 app.run()之后,会调用__call__()方法,看一下他的源码
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
就这么多,这里的self就是我们的app,一旦调用了app的wsgi_app方法,我们就不能插足了,所以中间件要在调用这个之前写。怎么实现呢,就是
from flask import Flask,flash,get_flashed_messages,request
app = Flask(__name__)
class MyMiddleware:
def __init__(self,wsgi_app):
#实例化对象的时候就会走这个方法,传进来的wsgi_app是原生的,复制给了对象的属性
self.wsgi_app=wsgi_app
#类的__call__,对象加括号的时候会调用,在上面的源码里,就是self.wsgi_app(environ, start_response),这里的self.wsgi_app是MyMiddleware类的对象,所以会执行这里的__Call__
def __call__(self, environ, start_response):
#自己想要通过中间件实现的功能就可以写在这里了。
print("123")
res=self.wsgi_app(environ, start_response)
print("456")
print(res)
return res
@app.route('/index')
def index():
# request.method
# session['sd']
return "ssdsdsdfsd"
if __name__ == '__main__':
#代码从这里开始走,先实例化一个自己生成的类的对象,把原来的app.wsgi_app当做参数传给类的__init__,生成的对象赋值给新的app.wsgi_app
app.wsgi_app = MyMiddleware(app.wsgi_app)
app.run()
LOCAL对象
我们在使用多线程的时候会有一个问题,那就是数据不安全问题
from threading import Thread
import time
cxw = -1
def task(arg):
global cxw
cxw = arg
time.sleep(2)
print(cxw)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
打印结果发现,所有的cxw都是9,按照我们原来的想法结果应该是0123456789这样,但是睡了两秒之后,最后一个线程早就把cxw变成9了。
通常解决这种问题我们会使用线程锁,但是也可以用local来实现
导入的local
from threading import Thread
from threading import local
import time
# 特殊的对象
cxw = local()
def task(arg):
# 对象.val = 1/2/3/4/5
cxw.value = arg
time.sleep(2)
print(cxw.value)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
这时候打印出来的内容就是正确的了,但不一定是按顺序的(cpu的调度问题)。那么这个到底是怎么实现的。
原理是类似于字典一样的存取值方式。获取当前的线程的id,来存入对应的值,取的时候也是同样,根据线程的id值来取,这样就实现了。
from threading import get_ident,Thread
import time
storage = {}
def set(k,v):
#获取当前线程的id
ident = get_ident()
#判断当前这个线程的id存不存在于这个storage,存在的话就设置一个值
if ident in storage:
#这里设置完之后结果类似于 {"pid1号":{k:v},}
storage[ident][k] = v
else:
#不存在这个pid号的话,就设置一个
storage[ident] = {k:v}
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
面向对象的形式:
面向对象的形式
from threading import get_ident,Thread
import time
class Local(object):
storage = {}
def set(self, k, v):
ident = get_ident()
if ident in Local.storage:
Local.storage[ident][k] = v
else:
Local.storage[ident] = {k: v}
def get(self, k):
ident = get_ident()
return Local.storage[ident][k]
obj = Local()
def task(arg):
obj.set('val',arg)
v = obj.get('val')
print(v)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
以上版本均有问题,以下才是最终版
最终版:
try:
#不仅可以实现多线程,还可以实现协程
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import get_ident
from threading import Thread
# from threading import get_ident,Thread
import time
class Local(object):
#为什么要把初始化strorage的语句放在__init__里面,也就是说把它变为对象的属性,而不是类的属性,因为一个项目可能在别的地方也会调用 Local,这时候大家用的就是共同的storage了,所以要把它设置为对象的属性
def __init__(self):
#为什么这里要调用功父类的__setattr__?因为如果直接用self.storage={}的话,会调用__setattr__,在自己的__setattr__里面的self.storage会调用__getattr_,然后在__getattr__中会发生递归,不停地调用自己。所以这里要调用父类的__setattr__,就像我们以前正常的赋值(dic['a']=1)一样。
object.__setattr__(self,'storage',{})
#self.storage={}
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()