我们知道Flask在处理一个请求时,wsgi_app()这个方法会被执行。而在Flask的源码内部request和current_app是通过_request_ctx_stack这个栈结构来保存的,分别为
# context locals _request_ctx_stack = LocalStack() current_app = LocalProxy(lambda: _request_ctx_stack.top.app) request = LocalProxy(lambda: _request_ctx_stack.top.request) session = LocalProxy(lambda: _request_ctx_stack.top.session) g = LocalProxy(lambda: _request_ctx_stack.top.g)需要注意最新的版本源码会有些不同request和current_app分别是有两个栈结构来存储:_request_ctx_stack和_app_ctx_stack。但新旧代码思路是差不多的。
最新的源码里,全局变量的定义
# context locals _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, "request")) session = LocalProxy(partial(_lookup_req_object, "session")) g = LocalProxy(partial(_lookup_app_object, "g"))其中_find_app和_lookup_app_object方法是这样定义的
def _find_app(): top = _app_ctx_stack.top if top is None: raise RuntimeError(_app_ctx_err_msg) return top.app def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) def _lookup_app_object(name): top = _app_ctx_stack.top if top is None: raise RuntimeError(_app_ctx_err_msg) return getattr(top, name)可以看到current_app和g是LocalProxy通过_app_ctx_stack.top进行封装的。request和session是_request_ctx_stack的封装。LocalProxy是werkzeug库中local对象的代理。LocalStack顾名思义是一个实现了栈的数据结构。
前面提到全局变量是跟线程绑定的,每个线程都有一个独立的内存空间,在A线程设置的变量,在B线程是无法获取的,只有在A线程中才能获取到这个变量。这个在Python的标准库有thread locals的概念。
然而在Python中除了线程外还有进程和协程可以处理并发程序的技术。所以为了解决这个问题Flask的依赖库werkzeug就实现了自己的本地变量werkzeug.local。它的工作机制跟线程本地变量(thread locals)是类似的。
要使用werkzug.local
from werkzeug.local import Local, LocalManager local = Local() local_manager = LocalManager([local]) def application(environ, start_response): local.request = request = Request(environ) ... application = local_manager.make_middleware(application)在application(environ,start_response)方法中就把封装了请求信息的request变量绑定到了local变量中。然后在相同的上下文下例如在一次请求期间,就可以通过local.request来获取到这个请求对应的request信息。
同时还可以看到LocalManager这个类,它是本地变量管理器,它可以确保在请求结束之后及时的清理本地变量信息。
在源码中对LocalManager是这样注释的
Local objects cannot manage themselves. For that you need a local
manager. You can pass a local manager multiple locals or add them later
by appending them to manager.locals. Every time the manager cleans up,
it will clean up all the data left in the locals for this context.
Local不能自我管理,需要借助LocalManager这个管家来实现请求结束后的清理工作。
0x05 总结current_app、g、request和session是Flask中常见4个全局变量。current_app是当前Flask服务运行的实例,g用于在应用上下文期间保存数据的变量,request封装了客户端的请求信息,session代表了用户会话信息。
0x06 学习资料https://werkzeug.palletsprojects.com/en/0.15.x/local/