一、flask的上下文管理
与threading.local实现方式相同,创建一个字典{线程或协程唯一标识:要存的数据},保证了数据的隔离;
而django/tornado框架是通过传参的形式实现的。
(1)请求上下文流程
- 代码启动时,先对LocalStack、Local、LocalProxy三个类初始化
- 请求进来时:将请求的相关数据封装到了RequestContext中,通过LocalStack将对象添加到Local类中(每个线程协程独立空间存储),
_request_ctx_stack.local = { 唯一标识:{ "stack":[ctx, ] }, }
# ctx.app # 当前APP的名称
# ctx.request # Request对象(封装请求相关东西)
# ctx.session # 空
- 使用request: 如,print(request)会调用LocalProxy中的__str__方法,request.method会调用LocalProxy中的__getattr方法等,调用这些
方法就相当于执行了偏函数_lookup_req_object,通过LocalStack去Local中获取值(ctx)
- 请求终止:通过LocalStack中的pop方法将Local类中属于该请求的数据删除掉
(2)应用上下文流程(0.9版本之后才有的):就是为了将一个请求周期需要传递的值与request分开,
其与请求上下文流程相同,就是创建了LocalProxy类的新对象g=LocalProxy(partial(_lookup_app_object, 'g'))
- 代码启动时,需要对类LocalStack、Local、LocalProxy初始化
- 当请求到来时,也会将要传递的值封装到RequestContext中,通过LocalStack将对象添加到Local类中(每个线程协程独立空间存储),
_app_ctx_stack.local = { 唯一标识:{ "stack":[app_ctx, ] }, 唯一标识:{ "stack":[app_ctx, ] }, } #app_ctx=AppContext对象 #app_ctx.app:该app对象 #app_ctx.g:每个请求周期都会创建一个用于在请求周期中传递值的一个容器
- 使用,也会调用LocalProxy中对应方法,从而执行偏函数_lookup_app_object,通过LocalStack类从Local类中获取值(app_ctx)
- 请求终止时,通过LocalStack中的pop方法将Local类中属于该线程或协程的数据清除掉
二、flask上下文管理相关问题
- 多线程或协程如何实现
_request_ctx_stack.local = { 唯一标识1:{ "stack":[ctx, ] }, 唯一标识2:{ "stack":[ctx, ] }, ......多少个线程或协程,就有多少个唯一标识 } _app_ctx_stack.local = { 唯一标识1:{ "stack":[app_ctx, ] }, 唯一标识2:{ "stack":[app_ctx, ] }, ......多少个线程或协程,就有多少个唯一标识 }
- flask的local保存数据时,使用了列表创建出栈,为什么使用了栈
(1)写web程序,web运行环境;栈中永远保存一条数据,可以不用栈
(2)写脚本获取app信息时,可能存在app上下文嵌套关系,栈中会有多条数据,往出取的时候也是取的自己的数据(测试会用)
from flask import Flask,request,current_app,_app_ctx_stack app1=Flask('app01') app1.debug=False app2=Flask('app02') app2.debug=True with app1.app_context(): #__enter__方法-->push-->app_ctx添加到_app_ctx_stack.local print(_app_ctx_stack._local.__storage__) print(current_app.config['DEBUG']) with app2.app_context(): # __enter__方法-->push-->app_ctx添加到_app_ctx_stack.local print(_app_ctx_stack._local.__storage__) print(current_app.config['DEBUG'])
(3)Web访问多app应用时,上下文管理是如何实现?
-
- 多app应用:在找到路由之前分发好路由,是通过字符串分隔实现,而蓝图是找到路由之后再分发路由
from werkzeug.wsgi import DispatcherMiddleware from werkzeug.serving import run_simple from flask import Flask app1=Flask('app01') app2=Flask('app02') @app1.route('/index') def index(): return 'index' @app2.route('/index2') def index2(): return 'index2' # http://localhost:5000/index # http://localhost:5000/sec/index2 dm=DispatcherMiddleware(app1,{ '/sec':app2, })
if __name__ == "__main__":
app2.__call__
run_simple('localhost', 5000, dm)
-
- 上下文管理是如何实现
run_simple('localhost', 5000, dm) 会执行DispatcherMiddleware类的__call__方法
def __call__(self, environ, start_response): script = environ.get('PATH_INFO', '') path_info = '' while '/' in script: if script in self.mounts: app = self.mounts[script] break script, last_item = script.rsplit('/', 1) path_info = '/%s%s' % (last_item, path_info) else: app = self.mounts.get(script, self.app) original_script_name = environ.get('SCRIPT_NAME', '') environ['SCRIPT_NAME'] = original_script_name + script environ['PATH_INFO'] = path_info return app(environ, start_response)
返回app(environ,start_response),又调用Flask类的__call__方法