flask框架中的上下文本质上就是两个类,咱们能够先看一下他的初始化函数:
应用上下文web
class AppContext(object): """The application context binds an application object implicitly to the current thread or greenlet, similar to how the :class:`RequestContext` binds request information. The application context is also implicitly created if a request context is created but the application is not on top of the individual application context. """ def __init__(self, app): self.app = app self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() # Like request context, app contexts can be pushed multiple times # but there a basic "refcount" is enough to track them. self._refcnt = 0
请求上下文flask
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 self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = [] self.match_request()
详细解释可参考:
https://blog.tonyseek.com/pos...服务器
相似Thread Local ,每一个线程对一个 Thread Local 对象的修改都不会影响其余线程。这种对象的实现原理也很是简单,只要以线程的 ID 来保存多份状态字典便可,就像按照门牌号隔开的一格一格的信箱。session
from werkzeug.wsgi import DispatcherMiddleware from biubiu.app import create_app from biubiu.admin.app import create_app as create_admin_app application = DispatcherMiddleware(create_app(), { '/admin': create_admin_app() })
flask上下文机制的实现基于 Werkzeug 的 Local Stack 。
阅读源码,Local Stack又依赖于local类,咱们发现Local类的本质是一个字典和一个获取到线程id的函数。数据结构
class Local(object): __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident)
而LocalStack在Local类的基础上又实现了栈的功能。多线程
class LocalStack(object): def __init__(self): self._local = Local()
flask中应用上下文栈和请求上下文栈正是基于上面的LocalStack类app
_request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack()
下面咱们经过一些实验来进一步学习flask的执行过程:框架
In [1]: from flask.globals import _app_ctx_stack, _request_ctx_stack In [2]: from flask import Flask In [3]: app = Flask(__name__) In [4]: _app_ctx_stack._local.__storage__ Out[4]: {} In [5]: _request_ctx_stack._local.__storage__ Out[5]: {} In [6]: req_ctx = app.test_request_context() In [7]: req_ctx.push() In [8]: _request_ctx_stack._local.__storage__ Out[8]: {<greenlet.greenlet at 0x7f69d45b8f20>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}} In [9]: _app_ctx_stack._local.__storage__ Out[9]: {<greenlet.greenlet at 0x7f69d45b8f20>: {'stack': [<flask.ctx.AppContext at 0x7f69d4774cf8>]}}
咱们能够看到一开始上下文均为空,test_request_context()函数会生成一个请求上下文,咱们经过push让它入栈,以后两个上下文都有了内容,为何_app_ctx_stack中也有内容呢?
咱们能够看一下源码,第一次请求上下文push时,app_ctx若是为None,就会调用_implicit_app_ctx_stack_添加一个应用上下文。ide
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)
这里咱们就能够解释flask中wsgi_app这部分代码,每次web服务器为flask提供了http请求的environ变量,flask就会建立一个request_context对象,执行push()
以后上下文对象就会进入_request_ctx_stack中,并在执行处理函数以后会自动pop,
因此咱们说请求上下文的生命周期就是一次请求的过程。函数
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) error = None try: try: ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
总体流程可参考:
https://www.jianshu.com/p/2a2...