博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
flask上下文管理源码
阅读量:6555 次
发布时间:2019-06-24

本文共 6633 字,大约阅读时间需要 22 分钟。

一、先说一下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属性就是偏函数partialrequest = 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.topif 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出去

 

转载于:https://www.cnblogs.com/wanghl1011/articles/8657747.html

你可能感兴趣的文章
003-诠释 Java 工程师【一】
查看>>
浅析rune数据类型
查看>>
普通用户开启AUTOTRACE 功能
查看>>
1034 - Navigation
查看>>
Bind+Nginx实现负载均衡
查看>>
游侠原创:推荐一款免费的Syslog转发工具
查看>>
巧用Zabbix自定义监控Mysql性能状态
查看>>
UIKeyboard键盘相关知识点-IOS开发
查看>>
你真的会 snapshot 吗? - 每天5分钟玩转 OpenStack(163)
查看>>
onAttachedToWindow和onDetachedFromWindow调用时机源码解析
查看>>
虚拟机外接USB设备情况的vMotion问题
查看>>
Mysql数据库大小查询
查看>>
#78 Reimplement Trampoline
查看>>
使用Java制作图文验证码
查看>>
java学习笔记----之多线程开发
查看>>
使用javap分析return和finally的执行字节码
查看>>
java 代理
查看>>
数据库设计三范式
查看>>
Eclipse插件开发- view to view drag drop
查看>>
Linux 技巧:让进程在后台可靠运行的几种方法
查看>>