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()

相关推荐