flask上下文管理源码

一、先说一下threading.local

因为flask是支持多线程的,并发就带来了对数据共享的冲突问题,threading.local的解决思路就是为每一个线程开辟空间来保存其特有的值,也就是将线程或协程的唯一标识作为key,key对应的value就是当前线程中的值,依据这个思路,flask中的Local类就是依据threading.local来实现每个线程都有自己的空间来保存值

Local类中封装了__storage__和__ident_func__属性,分别是空字典和唯一标识,用来保存线程特有的值

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

 

二、项目启动时,globals.py文件中会实例LocalStack和LocalProxy对象

LocalStack对象会封装_local属性,就是一个Local类对象,帮我们保存线程特有值

LocalProxy对象会封装__local私有属性,是一个函数,偏函数的话就是帮我们传递参数

g是每一个请求周期都会创建的用来保存传递值的一个容器

current_app当我们打印它的时候实际上就是app对象,首先还是会调用LocalProxy的__str__方法,这个方法又会去调用偏函数执行拿到当前请求对应的AppRequest对象,返回这个对象的app属性值也就是app对象

# LocalStack对象封装了_local()属性,就是一个local对象
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()

#  LocalProxy中的__local属性就是_find_app函数
current_app = LocalProxy(_find_app)
#  LocalProxy中的__local属性就是偏函数partial
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

 

三、请求到来时

1、触发app对象的__call__方法,继而执行app对象的wsgi_app方法

2、执行app对象的request_context方法,参数environ代表请求的所有数据

ctx = self.request_context(environ)

2.1、request_context方法返回RequestContext对象,执行init方法,这个对象中会对request进行二次封装,app就是Flask的app对象,还会封装flashes和session

class RequestContext(object):
    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None

2.2、对request进行二次封装,app.request._class是Request类,参数environ是请求的所有数据,这个Request对象就包含了请求的所有内容

3、返回app对象的wsgi_app方法中继续执行ctx的push方法,也就是执行RequestContext的push方法

ctx.push()

3.1,在RequestContext的push方法中,首先获取_app_ctx_stack的top方法返回值,即执行LocalStack中的top方法,这个方法会去Local中查找当前线程唯一标识对应的字典中的stack的列表中的值,此时还未赋值,所以返回None,继续向下执行self.app.app_context()也就是app对象的app_context方法,这个方法返回AppContext对象,参数是app对象,执行init方法

app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
      app_ctx = self.app.app_context()
      app_ctx.push()
      self._implicit_app_ctx_stack.append(app_ctx)
else:
      self._implicit_app_ctx_stack.append(None)
class AppContext(object):
    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()

3.2、返回的app_ctx就是AppContext对象,执行它的push方法,在这个方法中又会调用_app_ctx_stack的push方法,也就是LocalStack的push方法,参数就是app_ctx

3.3、在LocalStack的push方法中,先找到在Local中当前线程的唯一标识对应的字典,若这个字典中没有stack这个key就把一个空列表作为value值添加,然后把参数app_ctx也就是当前请求对应的AppContext对象追加到这个列表中,到这里app_ctx.push()就执行完了,注意这里的字典就是_app_ctx_stack的_local属性也就是Local对象的__storage__属性值,在这个字典中添加键值对

3.4、回到ctx的push方法中继续向下执行_request_ctx_stack的push方法,这里的self就是ctx(RequestContext对象),也就是LocalStack中的push方法,依然还是把当前请求对应的RequestContext对象保存在当前线程唯一标识对应字典中的stack列表中,注意这里的字典是_request_ctx_stack的_local属性Local对象的字典,和3.3步骤中字典是不一样的,到这里ctx的push方法就执行完了

_request_ctx_stack.push(self)

4、回到app对象的wsgi_app方法继续向下执行full_dispatch_request方法

4.1、在full_dispatch_request会先调用try_trigger_before_first_request_functions方法,方法中会会循环app对象的before_first_request_funcs属性列表,依次执行列表中的函数,也就是被@app.before_first_request装饰的函数,注意这里在锁的外部和内部都做了相同的判断,目的是为了多线程并发时能够保障被before_first_request装饰的函数只执行依次

    def try_trigger_before_first_request_functions(self):
        """Called before each request and will ensure that it triggers
        the :attr:`before_first_request_funcs` and only exactly once per
        application instance (which means process usually).

        :internal:
        """
        if self._got_first_request:
            return
        with self._before_request_lock:
            if self._got_first_request:
                return
            for func in self.before_first_request_funcs:
                func()
            self._got_first_request = True

4.2、回到full_dispatch_request继续向下执行preprocess_request方法,在preprocess_request方法中首先得到当前请求对应的蓝图对象会将app.before_request_funcs中key为None和蓝图对象name属性值对应的列表做chain链式操作得到一个新的列表,列表中是全局app和蓝图中被@app.before_request装饰的函数,继而循环这个新列表,依次执行每一个函数若没有得到蓝图对象,则只循环before_request_funcs中key为None的列表

    def preprocess_request(self):
        bp = _request_ctx_stack.top.request.blueprint

        funcs = self.url_value_preprocessors.get(None, ())
        if bp is not None and bp in self.url_value_preprocessors:
            funcs = chain(funcs, self.url_value_preprocessors[bp])
        for func in funcs:
            func(request.endpoint, request.view_args)

        funcs = self.before_request_funcs.get(None, ())
        if bp is not None and bp in self.before_request_funcs:
            funcs = chain(funcs, self.before_request_funcs[bp])
        for func in funcs:
            rv = func()
            if rv is not None:
                return rv

 

4.3、回到full_dispatch_request继续向下执行dispatch_request方法,这个方法就是在执行相应的视图了

4.3.1、执行视图函数

4.3.2、在视图中打印request会调用LocalProxy的__str__方法,方法中会调用_get_current_object()方法,这个方法中会调用实例request对象时传递的偏函数,偏函数中会调用_request_ctx_stack的top方法,也就是LocalStack的top方法,top方法就会获取stack属性值也就是列表,取索引-1,也就是当前线程或协程对应的RequestContext对象,并且获取这个RequestContext对象的request属性值,也就是之前二次封装的Request对象,打印request就是打印Request类对象

    @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

 

4.3.3、在视图打印request.method会调用LocalProxy的__getsttr__方法,在LocalProxy的getattr中,还是会调用equest的_get_current_object方法得到Request对象,获取这个对象的method值

4.4、视图执行完了

4.5、回到app对象的full_dispatch_request继续向下执行finalize_request方法,参数是视图返回的响应对象,在process_response方法中还是会先得到蓝图对象,并且将app.after_request_funcs字典中key为None和蓝图对象name属性值的列表做reversed(是为了执行顺序和文件加载顺序相反),再做链式操作chain得到一个新的列表,循环这个新的列表依次执行每个函数,也就是全局app和蓝图中被@app.after_request装饰的函数并返回响应对象

    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            response = self.process_response(response)
            request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception('Request finalizing failed with an '
                                  'error while handling an error')
        return response

 

四、请求结束后

1、也就是app对象的full_dispatch_request方法执行完后没有错误,继续向下就会执行ctx的auto_pop方法,执行RequestContext的auto_pop方法,会调用ctx的pop方法,在ctx的pop方法中会调用_request_ctx_stack(localstack对象)的pop方法,在localstack的pop方法中会找到stack属性对应的列表并删掉,也就是把当前线程或协程对应的RequestContext对象ctx删掉,往后又会执行app_ctx的pop方法,也就是执行AppRequest的pop方法,在这个方法中又会执行_app_ctx_stack的pop方法也就是LocalStack的pop方法,也就是把当前请求对应的AppRequest对象pop出去

 

posted @ 2018-03-27 15:33  九二零  阅读(164)  评论(0编辑  收藏  举报