• flask上下文管理


    一、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__方法

  • 相关阅读:
    ora-01847:月份中日的值必须介于 1 和当月最后一日之间
    (转)ORACLE中关于外键缺少索引的探讨和总结
    (转) Oracle性能优化-读懂执行计划
    shutdown immediate 持久无法关闭数据库之解决方案
    Oracle11g adump目录下面.aud增长导致空间撑满无法删除导致CRS无法启动的解决方法
    linux几种常见的文件内容查找和替换命令
    unzip解压3G或者4G以上文件失败的解决方法
    IMP-00058: ORACLE error 1882 encountered
    AIX文件系统/var空间100%的问题
    html5手机网站需要加的那些meta/link标签,html5 meta全解(转)
  • 原文地址:https://www.cnblogs.com/meng0410/p/8659276.html
Copyright © 2020-2023  润新知