zoukankan      html  css  js  c++  java
  • flask请求上下文

    先看一个例子:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
    
    # local_values = threading.local()
    
    
    class Foo(object):
        def __init__(self):
            self.name = 0
    
    
    
    local_values = Foo()
    
    def func(num):
        local_values.name = num
        import time
        time.sleep(1)
        print(local_values.name,threading.current_thread().name)
    
    
    for i in range(5):
        th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
        th.start()
    4 线程0
    4 线程1
    4 线程3
    4 线程2
    4 线程4
    

      

    上述结果不是我们想要的,local_values.name的值被最后一个覆盖了.............................

    flask的request和session设置方式比较新颖,如果没有这种方式,那么就只能通过参数的传递。

    flask是如何做的呢?

    1. 本地线程,保证即使是多个线程,自己的值也是互相隔离。

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import threading
     
    local_values = threading.local()
     
     
    def func(num):
        local_values.name = num
        import time
        time.sleep(1)
        print(local_values.name, threading.current_thread().name)
     
     
    for i in range(20):
        th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
        th.start()
    from _thread import  get_ident
    import threading
    
    
    
    def task(num):
        print(get_ident())
    
    for i in range(10):
        th = threading.Thread(target=task,args=(i,),name='线程%s' % i)
        th.start()
    View Code

    如上图所示,每个线程都有一个唯一标识

    __setattr__、__getattr__的用法
    class Foo(object):
        def __init__(self):
            pass
    
        def __setattr__(self, key, value):
            print(key,value)
    
    
        def __getattr__(self, item):
            print(item)
    
    obj = Foo()
    
    obj.xxx=123
    obj.xxx
    __setattr__ __getattr__

    自定义Local

     1 import threading
     2 try:
     3     from greenlet import getcurrent as get_ident # 协程
     4 except ImportError:
     5     try:
     6         from thread import get_ident
     7     except ImportError:
     8         from _thread import get_ident # 线程
     9 
    10 
    11 class Local(object):
    12 
    13     def __init__(self):
    14         #递归执行__setattr__
    15         # self.__storage__={}
    16         # self.__ident_func__=get_ident
    17         object.__setattr__(self, '__storage__', {})
    18         object.__setattr__(self, '__ident_func__', get_ident)
    19 
    20 
    21     def __getattr__(self, name):
    22         try:
    23             return self.__storage__[self.__ident_func__()][name]
    24         except KeyError:
    25             raise AttributeError(name)
    26 
    27     def __setattr__(self, name, value):
    28         ident = self.__ident_func__()
    29         storage = self.__storage__
    30         try:
    31             storage[ident][name] = value
    32         except KeyError:
    33             storage[ident] = {name: value}
    34 
    35     def __delattr__(self, name):
    36         try:
    37             del self.__storage__[self.__ident_func__()][name]
    38         except KeyError:
    39             raise AttributeError(name)
    40 
    41 
    42 local_values = Local()
    43 
    44 
    45 def task(num):
    46     local_values.name = num
    47     import time
    48     time.sleep(1)
    49     print(local_values.name, threading.current_thread().name)
    50 
    51 
    52 for i in range(5):
    53     th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
    54     th.start()
    55 
    56 
    57 
    58 执行结果:
    59 0 线程0
    60 1 线程1
    61 2 线程2
    62 4 线程4
    63 3 线程3
    View Code

    2. 上下文原理

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from functools import partial
    from flask.globals import LocalStack, LocalProxy
     
    ls = LocalStack()
     
     
    class RequestContext(object):
        def __init__(self, environ):
            self.request = environ
     
     
    def _lookup_req_object(name):
        top = ls.top
        if top is None:
            raise RuntimeError(ls)
        return getattr(top, name)
     
     
    session = LocalProxy(partial(_lookup_req_object, 'request'))
     
    ls.push(RequestContext('c1')) # 当请求进来时,放入
    print(session) # 视图函数使用
    print(session) # 视图函数使用
    ls.pop() # 请求结束pop
     
     
    ls.push(RequestContext('c2'))
    print(session)
     
    ls.push(RequestContext('c3'))
    print(session)
    View Code

    3. Flask内部实现

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    from greenlet import getcurrent as get_ident
     
     
    def release_local(local):
        local.__release_local__()
     
     
    class Local(object):
        __slots__ = ('__storage__', '__ident_func__')
     
        def __init__(self):
            # self.__storage__ = {}
            # self.__ident_func__ = get_ident
            object.__setattr__(self, '__storage__', {})
            object.__setattr__(self, '__ident_func__', get_ident)
     
        def __release_local__(self):
            self.__storage__.pop(self.__ident_func__(), None)
     
        def __getattr__(self, name):
            try:
                return self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
     
        def __setattr__(self, name, value):
            ident = self.__ident_func__()
            storage = self.__storage__
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}
     
        def __delattr__(self, name):
            try:
                del self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
     
     
    class LocalStack(object):
        def __init__(self):
            self._local = Local()
     
        def __release_local__(self):
            self._local.__release_local__()
     
        def push(self, obj):
            """Pushes a new item to the stack"""
            rv = getattr(self._local, 'stack', None)
            if rv is None:
                self._local.stack = rv = []
            rv.append(obj)
            return rv
     
        def pop(self):
            """Removes the topmost item from the stack, will return the
            old value or `None` if the stack was already empty.
            """
            stack = getattr(self._local, 'stack', None)
            if stack is None:
                return None
            elif len(stack) == 1:
                release_local(self._local)
                return stack[-1]
            else:
                return stack.pop()
     
        @property
        def top(self):
            """The topmost item on the stack.  If the stack is empty,
            `None` is returned.
            """
            try:
                return self._local.stack[-1]
            except (AttributeError, IndexError):
                return None
     
     
    stc = LocalStack()
     
    stc.push(123)
    v = stc.pop()
     
    print(v)
    View Code

    ------------------------------------------源码分析(重点):----------------------------------------------------------------------

    首先看这段代码:

    if __name__ == '__main__':
    	# 1.1
        app.__call__
        app.request_class
        response_class = Response
        app.run()
    

    在上面提到了上下文的原理:

    -----------------------LocalStak-----------------Local-----------------------------------  

    globals.py中有这么段代码,定义了request

    # flaskglobals.py
    # 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'))
    

      

    # Local类

    class Local(object):
        __slots__ = ('__storage__', '__ident_func__')
    
        def __init__(self):
            object.__setattr__(self, '__storage__', {})
            object.__setattr__(self, '__ident_func__', get_ident)
    
        def __getattr__(self, name):
            try:
                return self.__storage__[self.__ident_func__()][name]
            except KeyError:
                raise AttributeError(name)
    
        def __setattr__(self, name, value):
            ident = self.__ident_func__()
            storage = self.__storage__
            """
            {
    			唯一标识(__ident_func__):{
    				stack:[ctx/requestcontext,]//栈
    			}
            }
            """
            try:
                storage[ident][name] = value
            except KeyError:
                storage[ident] = {name: value}
    

      

    # LocalStack类

    def __init__(self):
        self._local = Local()
    
    # LocalStack()就是我们之前写过的一个多线程数据相互隔离的类,每个线程都有一个唯一标识
    # 每个线程都有一个独立的存储数据和访问数据的空间
    # __storage__   __ident_func__
    
    # werkzeuglocal.py
    # Local类  每个线程唯一标识 可以存储数据
    # LocalStack类 实例化Local类
    
    # LocalStack类
    def push(self, obj):
        # 刚开始stack=[]
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)//执行Local的__setattr__方法
        return rv
    
    def pop(self):
        stack = getattr(self._local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()
    

      

    以上都是启动会加载的文件 ,创建唯一标识,并给这个标识赋值 stack=[],stack是个栈,存储着每个用户请求的对象(数据)RequestContext对象(ctx)

    {
    唯一标识(__ident_func__):{
    stack:[ctx/requestcontext,]//栈
    }
    }
    

      

    --------------------------------接下来开始看看执行流程:---------(app)-----------------------------------------------

    app.__call__
    app.request_class
    response_class = Response
    app.run()

    执行app.__call__方法 

    app.run()    =》run_simple(host, port, self, **options) =》app.__call__

    # flaskapp.py
    # 主要从这里入手 分析代码
    # Flask类(app)

     app.py

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
    

    app.py

    def wsgi_app(self, environ, start_response):
        #将请求相关的数据environ封装到了RequestContext对象中
        #再将对象封装到Local中(每个线程/每个协程独立空间存储)
        #request_context对象中有如下:
            #ctx.app 当前APP
            #ctx.request Request对象(封装请求相关的东西)
            #ctx.session
            #RequestContext类  ctx.py
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                #RequestContext.push()->LocalStack.push
                #将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
                #_request_ctx_stack = LocalStack()
                 """
                    {
                        唯一标识(__ident_func__):{
                            stack:[ctx/requestcontext,]//
                        }
                    }
                """
                # 将ctx通过LocalStack添加到Local中
                # 此时ctx中的session中已经有值了
                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
            #RequestContext.pop()->LocalStack.pop(_request_ctx_stack.pop())
            #_request_ctx_stack = LocalStack()
            #最后将自己请求在Local中保存的数据清除
            ctx.auto_pop(error)

     ctx = self.request_context(environ) 记住了!!!

    def request_context(self, environ):
      #self=app return RequestContext(self, environ)
    
    

    ctx就是request_context对象

    #request_context对象中有如下属性:
      #ctx.app 当前APP
      #ctx.request Request对象(封装请求相关的东西)
      #ctx.session

      

    ctx.push():

    #执行RequestContext.push()->LocalStack.push
    #将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
    """
    {
    唯一标识(__ident_func__):{
    stack:[ctx/requestcontext,]//栈
    }
    }
    """

     

    --------------------------RequestContext------------ctx.py-------------------------------------

    # flaskctx.py
    # RequestContext类
    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()
    
    
    request = app.request_class(environ)
    
    
    
    
    #ctx.push
    def push(self):
    
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)
    
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            # 应用上下文
            # app_ctx = AppContext对象
            # app_ctx.app = Flask对象
            # app_ctx.g=对象(用于一个周期内要保存的值,相当于全局变量)
            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)
    
        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()
    
        #selft 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
        #_request_ctx_stack = LocalStack()
        #相当于执行LocalStack.push()
        #self 就是RequestContext类
        _request_ctx_stack.push()
    
        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(
                self.app, self.request
            )
    
            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

    RequestContext对象中有request,session等对象,#ctx.push执行的就是RequestContext.push,其中有这么段代码

     _request_ctx_stack.push()
    	#self 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
    	#_request_ctx_stack = LocalStack()
    	#相当于执行LocalStack.push()
    	#self 就是RequestContext类
          _request_ctx_stack.push()
    

      

    组中执行的是Local中 storage[ident][name] = value

    最终目的就是把RequestContext对象也就是ctx放到localStack中的唯一标识字典中的stack栈中

    以上明白了吗?不明白可以去看看源码。。。。。  现在已经出现了

    ctx=RequestContext     app    LocalStack      Local     四个类

    globals.py  刚开始启动加载的文件

    到这里 请求上下文已经差不多了,每个请求都有一个独立数据存储空间,互补影响彼此。就是把request对象放到RequestContext.request属性里面,然后又把RequestContext对象放到每个线程(请求)或者是协程的一个唯一标识的stack栈中

    """
    storage:{
    唯一标识(__ident_func__):{
    stack:[ctx/requestcontext,]//栈
    }
    }
    """

    视图中执行print(request)发生了什么?

    --------------------------request-------------------------------------------------

    # flaskglobals.py
    # 视图中执行print(request)发生了什么?
    # 执行LocalProxy类中的__str__方法
    
    
    
    # 在globals.py文件中 (最开始在加载的文件)
    # 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'))
    
    # 偏函数做了啥?
    #partial(_lookup_req_object, 'request')
    
    
    # 函数_lookup_req_object  去LocalStack获取唯一标识栈里面的值(RequestContext对象)
    def _lookup_req_object (name):
        # _request_ctx_stack = LocalStack()
        # 返回的其实就是刚开始塞到每个线程唯一标识里面的那个ctx/requestContext对象
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        # 到requestContext对象取request属性值 request对象
        return getattr(top, name)
    
    
    
    # LocalProxy类中的__str__方法 最终就是执行_lookup_req_object函数而已
    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    
    
    
    
    
    在LocalStack类中:
        @property
        def top(self):
            """The topmost item on the stack.  If the stack is empty,
            `None` is returned.
            """
            try:
                return self._local.stack[-1]
            except (AttributeError, IndexError):
                return None
    
        def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            # 执行_lookup_req_object函数
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)
    request = LocalProxy(partial(_lookup_req_object, 'request'))
    这段代码要仔细看看了

    print(request)  =>       LocalProxy。__str__    =>       LocalProxy.__str__ = lambda x: str(x._get_current_object()) =>( _get_current_object 里面执行了 )self.__local()

    LocalProxy.__local  就是传入的偏函数(

    partial(_lookup_req_object, 'request')

    最后执行self.__local() 相当于执行偏函数,返回request对象了

    LocalProxy类中

        def __init__(self, local, name=None):
            #Local = partial(_lookup_req_object, 'request')
            #_local =  _LocalProxy__local私有字段访问
            object.__setattr__(self, '_LocalProxy__local', local)
            object.__setattr__(self, '__name__', name)
            if callable(local) and not hasattr(local, '__release_local__'):
                object.__setattr__(self, '__wrapped__', local)
    
        def _get_current_object(self):
            if not hasattr(self.__local, '__release_local__'):
                return self.__local()
            try:
                return getattr(self.__local, self.__name__)
            except AttributeError:

    _request_ctx_stack的应用

    # _request_ctx_stack的应用
    from flask.globals import _request_ctx_stack
    from functools import partial
    
    def _lookup_req_object(name):
        # name = request
        # top= ctx
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError('不存在')
        # return ctx.request
        return getattr(top, name)
    
    class Foo(object):
        def __init__(self):
            self.xxx = 123
            self.ooo = 888
    
    req = partial(_lookup_req_object,'xxx')
    xxx = partial(_lookup_req_object,'ooo')
    
    # 当前求刚进来时
    _request_ctx_stack.push(Foo())
    
    # 使用
    # obj = _request_ctx_stack.top
    # obj.xxx
    v1 = req()
    print(v1)
    v2 = xxx()
    print(v2)
    
    
    # 请求终止,将local中的值移除
    _request_ctx_stack.pop()
    _request_ctx_stack

    上下文
    - threading.Local和Flask自定义Local对象
    - 请求到来
    - ctx = 封装RequestContext(request,session)
    - ctx放到Local中
    - 执行视图时
    - 导入request
    - print(request) --> LocalProxy对象的__str__
    - request.method --> LocalProxy对象的__getattr__
    - request + 1 --> LocalProxy对象的__add__
    - 调用 _lookup_req_object函数:去local中将requestContext想获取到,再去requestContext中获取request或session
    - 请求结束
    - ctx.auto_pop()
    - ctx从local中移除。

    多app应用

    from werkzeug.wsgi import DispatcherMiddleware
    from werkzeug.serving import run_simple
    from flask import Flask, current_app
    
    app1 = Flask('app01')
    
    app2 = Flask('app02')
    
    
    
    @app1.route('/index')
    def index():
        return "app01"
    
    
    @app2.route('/index2')
    def index2():
        return "app2"
    
    # http://www.oldboyedu.com/index
    # http://www.oldboyedu.com/sec/index2
    dm = DispatcherMiddleware(app1, {
        '/sec': app2,
    })
    
    if __name__ == "__main__":
        app2.__call__
        run_simple('localhost', 5000, dm)
    View Code

    离线脚本:

    from flask import Flask,current_app,globals,_app_ctx_stack
    
    app1 = Flask('app01')
    app1.debug = False # 用户/密码/邮箱
    # app_ctx = AppContext(self):
    # app_ctx.app
    # app_ctx.g
    
    app2 = Flask('app02')
    app2.debug = True # 用户/密码/邮箱
    # app_ctx = AppContext(self):
    # app_ctx.app
    # app_ctx.g
    
    
    
    with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
        # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
        print(_app_ctx_stack._local.__storage__)
        print(current_app.config['DEBUG'])
    
        with app2.app_context():
            # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
            print(_app_ctx_stack._local.__storage__)
            print(current_app.config['DEBUG'])
    
        print(current_app.config['DEBUG'])
    View Code
    """
    需求:不用数据库连接池,显示数据库连接
    """
    class SQLHelper(object):
    
        def open(self):
            pass
    
        def fetch(self,sql):
            pass
    
        def close(self):
            pass
    
        def __enter__(self):
            self.open()
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.close()
    
    
    # obj = SQLHelper()
    # obj.open()
    # obj.fetch('select ....')
    # obj.close()
    
    
    with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值
        obj.fetch('xxxx')
        # 当执行完毕后,自动调用类 __exit__ 方法
    
    # 以后如果遇到:
    View Code
  • 相关阅读:
    帧锁定同步算法
    为 Raft 引入 leader lease 机制解决集群脑裂时的 stale read 问题
    etcd:从应用场景到实现原理的全方位解读
    给定一个二叉搜索树(BST),找到树中第 K 小的节点
    UDP如何实现可靠传输
    理解TCP/IP三次握手与四次挥手的正确姿势
    Redis持久化
    Redis提供的持久化机制(RDB和AOF)
    redis渐进式 rehash
    redis rehash
  • 原文地址:https://www.cnblogs.com/sunlong88/p/9606965.html
Copyright © 2011-2022 走看看