Flask请求上下文管理

1 偏函数

  • partial 使用该方式可以生成一个新函数

    from functools import partial
    
    def mod( n, m ):
    return n % m mod_by_100 = partial( mod, 100 ) # 100传给n print mod( 100, 7 ) # 2
    print mod_by_100( 7 ) # 2

2 线程安全

import time
from threading import local class Foo(local):
# 继承local,保证线程安全,也保证了处理速度,threading.local()这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问,
num = 0 foo = Foo()
def add(i):
foo.num =i
time.sleep(0.5)
print(foo.num) from threading import Thread
for i in range(20):
task = Thread(target=add,args=(i,))
task.start()

3 请求上下文

3.1 Flask请求上文

  1. 当请求进来时,app(), Flask实例化对象app执行__call__
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
  1. 执行wsgi_app 得到 一个 RequestContext的对象 ctx (封装了request以及session)

    ctx = self.request_context(environ)
    class RequestContext(object):
    #此时的self是RequestContext对象 -->ctx 中封装了request/session
    def __init__(self, app, environ, request=None):
    self.app = app #app = Flask对象
    if request is None:
    #请求的原始信息通过request_class后此时request已经存在,request.methods等
    request = app.request_class(environ)
    self.request = request
    self.url_adapter = app.create_url_adapter(self.request)
    self.flashes = None
    self.session = None
  2. ctx 执行 ctx.push():

    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)
  3. RequestContext对象的push方法

    def push(self):
    # _request_ctx_stack = LocalStack()一个LocalStack对象
    # _request_ctx_stack._local = LocalStack()._loacl = {"__storage__":{},"__ident_func__":get_ident}
    top = _request_ctx_stack.top
    #top =None
    if top is not None and top.preserved:
    top.pop(top._preserved_exc)
    • _ request_ctx_stack是一个LocalStack对象 ,LocalStack()._local是一个Local对象 即Local()
    class LocalStack(object):
    def __init__(self):
    self._local = Local()
    #self._loacl = {"__storage__":{},"__ident_func__":get_ident}
    • _request_ctx_stack中top方法,返回None (想哭,但是没有眼泪,这个方法,琢磨了半个小时)
  4. Local对象经过初始化得到的字典值

    class Local(object):
    #限定键槽,当前只能由两个属性值__storage__,__ident_func__
    __slots__ = ('__storage__', '__ident_func__') def __init__(self):
    object.__setattr__(self, '__storage__', {})
    object.__setattr__(self, '__ident_func__', get_ident) # {"__storage__":{},"__ident_func__":get_ident} #此时get_dient 是个没有执行的函数,内存地址
    • _request_ctx_stack中top方法,返回None (第二次不会上当)
    @property
    def top(self):
    """The topmost item on the stack. If the stack is empty,
    `None` is returned.
    """
    try:
    # self._local 即Local对象调用__getattr__方法
    #在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
    # [ctx->request/session]
    return self._local.stack[-1]
    #得到ctx对象
    except (AttributeError, IndexError):
    return None
  5. _request_ctx_stack 对象执行push方法

    _request_ctx_stack.push(self)  #当前的self为ctx
    def push(self, obj):
    #此时的self是LocalStack对象, obj为ctx
    """Pushes a new item to the stack"""
    # self._local = {"__storage__":{},"__ident_func__":get_ident}
    #找不到返回值是None
    rv = getattr(self._local, 'stack', None)
    if rv is None:
    #由于.stack后面有等号,执行的时候Local()对象的__setattr__方法
    #实际上是地址的赋值,此时stack和rv都指向空列表
    self._local.stack = rv = []
    #{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
    rv.append(obj)
    # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
    # 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
    return rv
    #rv=[ctx->request/session]
    def __setattr__(self, name, value):
    #name=stack value=rv=[]
    #self是Local对象 {"__storage__":{},"__ident_func__":get_ident}
    ident = self.__ident_func__() #执行get_ident函数获取当前线程id 8080
    storage = self.__storage__ #storge ={8080:{stack:rv=[]}}
    try:
    storage[ident][name] = value
    except KeyError:
    storage[ident] = {name: value} #storage={} # {"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
    • 执行完push方法 请求上文结束:
    #当请求进来,第一件事就是要把当前这个请求在我服务器上的线程开辟一个空间(线程对应的空间,必须含有stack对应一个列表存放ctx(request/session)
    # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}

3.3.2Flask请求下文

  1. 导入request开始使用,在request中

    #此时request是一个函数包裹一个偏函数   LocalProxy()是一个代理
    #当前的request是一个LocalProxy() request.method 执行__getattr__方法
    request = LocalProxy(
    partial(_lookup_req_object, 'request') #return request对象
    )
  2. 在偏函数中 将request传入到 _lookup_req_object中: 此时得到一个request对象

    def _lookup_req_object(name):
    # _request_ctx_stack是LocalStack对象
    top = _request_ctx_stack.top
    #下文[ctx->request/session]
    if top is None:
    raise RuntimeError(_request_ctx_err_msg)
    #此时的name是request,从ctx对象中找出request对象
    return getattr(top, name)

    ...

    @property
    def top(self):
    """The topmost item on the stack. If the stack is empty,
    `None` is returned.
    """
    try:
    # self._local 即Local对象调用__getattr__方法
    #在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
    # [ctx->request/session]
    return self._local.stack[-1]
    #得到ctx对象
    except (AttributeError, IndexError):
    return None
    • 此时的top不是None已经存在值 (0.0)
  3. partial(_lookup_req_object, 'request') 这一层执行完得到一个reauest对象,将偏函数传入到LocalProxy中

    @implements_bool
    class LocalProxy(object):
    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__') def __init__(self, local, name=None):
    #local是request偏函数
    object.__setattr__(self, '_LocalProxy__local', local) #__local = request偏函数
    object.__setattr__(self, '__name__', name)
    #当前偏函数可以执行而且判断loacl中是否有 __release_local__ ==>这句话成立
    if callable(local) and not hasattr(local, '__release_local__'):
    # "local" is a callable that is not an instance of Local or
    # LocalManager: mark it as a wrapped function.
    object.__setattr__(self, '__wrapped__', local) #__warpped__还是local偏函数
  4. 当前的request是一个LocalProxy() request.method 执行LocalProxy中的__getattr__方法

        def __getattr__(self, name): # name是method(举例)
    if name == '__members__':
    return dir(self._get_current_object())
    #此时self._get_current_object()是经过_local 执行后得到的request对象,从request对象中去取出method
    return getattr(self._get_current_object(), name)

    ...

    def _get_current_object(self):
    #self._local是偏函数
    if not hasattr(self.__local, '__release_local__'):
    #执行偏函数,返回request对象
    return self.__local()
    try:
    return getattr(self.__local, self.__name__)
    except AttributeError:
    raise RuntimeError('no object bound to %s' % self.__name__)

3.3.3小结

由此看来,falsk上下文管理可以分为三个阶段:

  1. 请求上文 ->

    当请求进来,第一件事就是要把当前这个请求在服务器上的线程开辟一个空间(线程对应的空间,必须含有stack对应一个列表存放ctx(request/session),具体-->:将request,session封装在 RequestContext类中

    app,g封装在AppContext类中,并通过LocalStack将requestcontext和appcontext放入Local类中

    在local类中,以线程ID号作为key的字典,
  2. 请求下文:

    通过localproxy--->偏函数--->localstack--->local取值
  3. '请求响应时':-->要将上下文管理中的数据清除

    先执行save.session()再各自执行pop(),将local中的数据清除

详细看源码

3.4 应用上下文

  • 执行wsgi_app方法

       #ctx为一个RequestContext的对象,参数为environ
    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)
  • 执行push方法,_app_ctx_stack 同样是LocalStck对象,初始化时候top为None

        def push(self):
    app_ctx = _app_ctx_stack.top
    #app_ctx = None
    if app_ctx is None or app_ctx.app != self.app:
    app_ctx = self.app.app_context() #app_context是AppContext对象 与RequestContenx一样,知识序列化出app和g
    app_ctx.push()
    # 应用上文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
    self._implicit_app_ctx_stack.append(app_ctx)
    else:
    self._implicit_app_ctx_stack.append(None)
    • 执行app_ctx.push 进而 _app_ctx_stack.push
       def push(self):
    """Binds the app context to the current context."""
    self._refcnt += 1
    if hasattr(sys, 'exc_clear'):
    sys.exc_clear()
    #将AppContext存在LocalStack对象中
    _app_ctx_stack.push(self)
    appcontext_pushed.send(self.app)
        def push(self, obj):
    #此时的self是LocalStack对象, obj为ctx
    """Pushes a new item to the stack"""
    # self._local = {"__storage__":{},"__ident_func__":get_ident}
    #找不到返回值是None
    rv = getattr(self._local, 'stack', None)
    if rv is None:
    #由于.stack后面有等号,执行的时候Local()对象的__setattr__方法
    #实际上是地址的赋值,此时stack和rv都指向改空列表
    self._local.stack = rv = []
    #{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
    rv.append(obj)
    # {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
    # 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
    return rv
    #rv=[ctx->request/session]

    到此,push完毕,应用上文结束,应用下文在离线脚本时候使用,另外:在global.py中

def _find_app():
top = _app_ctx_stack.top #得到app_ctx(app / g)
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app #返回一个app即flask对象 只不过此时的flask对象 是公共的,与初始化的相同
# 但是是独立出来已经被配置好的Flask对象 # LocalStack是 针对当前这个线程对独立的Flask_app进行修改, 不影响现在运行的app =>离线脚本
#但是这个app 在请求结束后会从LocalStack中通过 __delattr__ 删除 # context locals
_request_ctx_stack = LocalStack() #LocalStark = self._loacl = {"__storage__":{},"__ident_func__":get_ident}
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app) # current_app可以点 .run | .route 等

Flask上下文管理机制流程(源码剖析)的更多相关文章

  1. Flask上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  2. 【Cocos2d-x 3.x】内存管理机制与源码分析

    侯捷先生说过这么一句话 :  源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...

  3. Python内存管理机制-《源码解析》

    Python内存管理机制 Python 内存管理分层架构 /* An object allocator for Python. Here is an introduction to the layer ...

  4. Struts2运行流程-源码剖析

    本文为原创,如需转载,请标明出处:http://www.cnblogs.com/gudu1/p/7726172.html 首先说一下查看这些框架源码的感受,每一次深入探究 Spring.Struts ...

  5. Android的Context Manager(服务管理器)源码剖析-android学习之旅(99)

    Context Manager介绍 Context Manager对应的进程是servicemanager进程,它先于Service Server和服务客户端运行,进入接收IPC数据的待机状态,处理来 ...

  6. flask你一定要知道的上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  7. Flask的上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  8. Redis源码剖析

    Redis源码剖析和注释(一)---链表结构 Redis源码剖析和注释(二)--- 简单动态字符串 Redis源码剖析和注释(三)--- Redis 字典结构 Redis源码剖析和注释(四)--- 跳 ...

  9. Flask核心机制--上下文源码剖析

    一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...

随机推荐

  1. poj 3468 A Simple Problem with Integers(原来是一道简单的线段树区间修改用来练练splay)

    题目链接:http://poj.org/problem?id=3468 题解:splay功能比线段树强大当然代价就是有些操作比线段树慢,这题用splay实现的比线段树慢上一倍.线段树用lazy标记差不 ...

  2. JOBDU 1108 堆栈的使用

    之所以把这道题目贴出来的原因,是因为真的有几个地方要注意的 题目1108:堆栈的使用 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:10763 解决:3119 题目描述: 堆栈是一种基本的 ...

  3. yzoj P1412 & 洛谷P1629 邮递员送信 题解

    有一个邮递员要送东西,邮局在结点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要一定的时间.这个邮递员每次只能带一 ...

  4. cesium页面小控件的隐藏

    cesium页面小控件的隐藏 1   创建一个Viewer var viewer = new Cesium.Viewer('cesiumContainer');//cesiumContainer为di ...

  5. JavaScript简单的弹幕

    弹幕 首先是弹幕的位置,是要从最右滑到最左,为了防止随机高度弹幕会覆盖的问题,设置了通道. 每一个通道是从左到右的一条,高度固定,这样不同通道的弹幕不会相互覆盖. 弹幕滑动就是简单设置CSS属性  t ...

  6. 关于git使用的几点理解

    1.git为分布式的版本控制系统,有远程仓库和本地仓库,远程仓库和本地仓库之间建立关联关系后,可将本地仓库的更新push(相当于是内容同步)到远程仓库进行保存,远程仓库的作用相当于一个最终代码备份的地 ...

  7. 纯JS实现在一个字符串b中查找另一个字符串a出现的所有位置,并且不使用字符串的方法(递归)

    问题:判断字符串A在中所有出现字符串B中(长度大于1)的索引.不得使用字符串方法indexof,substring等 有小伙伴在面试遇到了这个问题,乍一看如果使用使用字符串方法indexof,subs ...

  8. SpringBoot使用注解的方式构建Elasticsearch查询语句,实现多条件的复杂查询

    背景&痛点 通过ES进行查询,如果需要新增查询条件,则每次都需要进行硬编码,然后实现对应的查询功能.这样不仅开发工作量大,而且如果有多个不同的索引对象需要进行同样的查询,则需要开发多次,代码复 ...

  9. IOCAutofac与ORMEntityFramwork的联系--单例模式

    在你阅读之前默认你已经理解了IOC.DI.ORM以及autofac和EF的使用 在我最近写项目的时候我在单步调试时偶然发现的一个问题 先说明我的项目使用.NET MVC 三层架构,运用IOC Auto ...

  10. jquery ajax到servlet出现中文乱码(utf-8编码下)

    个人遇到的该问题有两大类: 第一类很普遍,就是jsp页面编码没有规定,servlet中接收参数没有转码,response没有使用setContentType()和setCharacterEncodin ...