flask基础之请求处理核心机制(五)

前言

总结一下flask框架的请求处理流程。

系列文章

WSGI协议

一般来说http服务器和框架需要进行解耦,http专门负责接受HTTP请求、解析HTTP请求、发送HTTP,响应请求等;而web框架负责处理请求的逻辑,和数据库的交互等等,那么它们之间需要约定一套接口使得http服务器能够调用web框架的处理逻辑,这个协议就是WSGI协议。

WSGI协议要求http服务器接收到http请求后经过处理得到两个参数,一个是请求数据封装的字典environ,另一个是需要框架回调的方法start_response。

在flask框架中,服务器对每个请求调用一次app的wsgi_app方法返回结果,而wsgi_app方法的执行过程就是请求的处理流程。

class Flask(object):
    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        ctx.push()
        error = None
        try:
            try:
                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
            ctx.auto_pop(error)

第一步:服务器启动

服务器启动后,假设服务器是基于线程的,此时app对象被创建,加载了相关的初始化参数,这时代理对象如current_app、g、session、request等会被创建,但是它们目前并没有代理任何的对象,如果此时使用它们会报错,需要在第一次接收到请求后才会真正地代理上下文。那么服务器启动究竟干了什么事呢?

详细请参考:flask之app初始化

第二步:接收请求,创建上下文,入栈

服务器收到一个http请求后,使用app上下文和请求数据创建一个线程,调用app的request_context(self, environ)方法,将解包后封装的http请求数据当做environ参数传入,返回一个RequestContext实例对象,每一个请求都有一个RequestContext实例对象,同时他们都拥有各自的app上下文,也就是说在本线程中的app应用是服务器初始化app的一个引,因此我们可以动态修改app的属性。

将RequestContext对象push进_request_ctx_stack里面,_request_ctx_stack是一个栈对象,此时代理对象request指向栈顶的RequestContext对象的request属性,该request是一个Request对象,而session此时指向栈顶的RequestContext对象的session属性。

判断_app_ctx_stack栈顶是否存在应用上下文对象AppContext,不存在就创建,同时将AppContext推送到_app_ctx_stack栈对象中,此时current_app指向栈顶AppContext对象的app属性,而g变量指向栈顶AppContext对象的g属性,本质上是一个_AppCtxGlobals对象,数据结构是一个字典。

  • 应用上下文和请求上下文存放的栈对象
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
  • 动态修改app的属性
from flask import Flask

app = Flask(__name__)

@app.route(‘/test1‘)
def test1():
    """
    动态添加一个视图函数
    """
    @app.route(‘/test2‘)
    def test2():
        return ‘test2‘
    return ‘OK‘
  • 应用上下文和请求上下文源码分析
class Flask(object):
    def app_context(self):
        return AppContext(self)
    def request_context(self, environ):
        return RequestContext(self, environ)

class AppContext(object):
    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()
    def push(self):
        self._refcnt += 1
        if hasattr(sys, ‘exc_clear‘):
            sys.exc_clear()
        _app_ctx_stack.push(self) # 将自己推送到栈中
        appcontext_pushed.send(self.app)

class RequestContext(object):
    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
    def push(self):
        pass

第三步:请求分派

分发请求并执行处理逻辑的函数为full_dispatch_request,其返回一个Response对象。处理的过程为:

  • 先执行app对象before_first_request_funcs列表中的所有方法,这是针对app的第一次请求需要的预处理方法,执行该列表中的所有方法是一个原子操作,被加了线程锁,如果不是第一次请求就跳过;
  • 然后执行app对象的url_value_preprocessors字典中对应蓝图的列表中的所有方法,对所有的URL进行预处理;
  • 执行app对象的before_request_funcs列表中的所有方法,其会按照加载的顺序链执行,并且如果中间有任何一个方法返回的结果不是None,那么执行中断,直接返回结果,不再执行视图函数。这是针对app所有的请求都会执行的方法,当然也可以通过蓝图来进行管理;
  • 通过request对象的url_rule(Rule)找到app中的url_map中对应的视图函数执行,返回一个元组的结果rv,就是我们平时写视图函数时返回的元组;
  • 调用make_response函数,以返回的结果rv作为参数构建一个Response对象;
  • 执行app对象中的after_request_funcs列表的所有方法,以构建的Response对象作为参数,每个方法必须都返回Response类型的对象,最后调用session保存本次的状态信息;

第四步:出栈

先执行app对象的teardown_request_funcs列表中的所有的方法,其方法和after_request_funcs中的一样,只不过是在出栈前才触发,这意味着即使处理逻辑的部分出错,这里方法也会执行,然后从_request_ctx_stack中弹出RequestContext请求上下文,然后执行app对象中的teardown_appcontext_funcs列表的所有方法,最后从_app_ctx_stack中弹出AppContext应用上下文。

class AppContext:
    def pop(self, exc=_sentinel):
        app_ctx = self._implicit_app_ctx_stack.pop() 

        try:
            clear_request = False
            if not self._implicit_app_ctx_stack:
                self.preserved = False
                self._preserved_exc = None
                if exc is _sentinel:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_request(exc) # 调用请求钩子
                if hasattr(sys, ‘exc_clear‘):
                    sys.exc_clear()

                request_close = getattr(self.request, ‘close‘, None)
                if request_close is not None:
                    request_close()
                clear_request = True
        finally:
            rv = _request_ctx_stack.pop() # 弹出请求上下文
            if clear_request:
                rv.request.environ[‘werkzeug.request‘] = None
            if app_ctx is not None:
                app_ctx.pop(exc) # 弹出应用上下文

flask请求处理最简代码模型

假设服务器使用的是多进程模式。

from multiprocessing import Process, Pool
class Flask(object):
    def __call__(self, environ, start_response):
        """定义app对请求的处理过程"""
        pass

def listen_port():
    """假设这是端口监听并解析http请求的方法"""
    pass

def run_web():
    """假设这是程序主循环"""
    app = Flask() # 创建一个app,这是app初始化做的
    pool = Pool(10)
    while True:
        # 获取一个http请求的数据
        environ, start_response = listen_port()
        # 调用app处理请求
        pool.apply_async(app, args=(environ, start_response))

if __name__ == ‘__main__‘:
    run_web()

总结

  • 无论是gunicorn服务器还是uwsgi服务器,其启动后加载了app对象;
  • 当收到http请求后,按照http协议解析数据,将数据打包成一个字典,将其和响应函数一起作为参数调用app对象的wsgi_app方法;
  • wsgi_app方法按照接收请求,创建上下文,入栈,请求分发,出栈的步骤处理完业务逻辑返回响应数据;

参考

  • http://docs.jinkan.org/docs/flask/reqcontext.html
  • https://dormousehole.readthedocs.io/en/latest/

原文地址:https://www.cnblogs.com/cwp-bg/p/9917239.html

时间: 2024-10-03 21:55:17

flask基础之请求处理核心机制(五)的相关文章

flask基础之AppContext应用上下文和RequestContext请求上下文(六)

前言 应用上下文和请求上下文存在的目的,官方文档讲的很清楚,可参考: http://www.pythondoc.com/flask/appcontext.html 应用上下文对象在没有请求的时候是可以单独存在的,但是请求上下文对象只有在收到请求后才会被创建.请求处理和应用上下文和请求上下文的关系是: 接收请求-->创建请求上下文-->请求上下文入栈-->创建该请求的应用上下文-->应用上下文入栈-->处理逻辑-->请求上下文出栈-->应用上下文出栈 系列文章 fl

flask基础之app初始化(四)

前言 flask的核心对象是Flask,它定义了flask框架对于http请求的整个处理逻辑.随着服务器被启动,app被创建并初始化,那么具体的过程是这样的呢? 系列文章 flask基础之安装和使用入门(一) flask基础之jijia2模板使用基础(二) flask基础之jijia2模板语言进阶(三) 一个最基本web应用的flask代码 from flask import Flask import os # 创建app对象 app = Flask(__name__,template_fold

Qt核心机制与原理

转:  https://blog.csdn.net/light_in_dark/article/details/64125085 ★了解Qt和C++的关系 ★掌握Qt的信号/槽机制的原理和使用方法 ★了解Qt的元对象系统 ★掌握Qt的架构 ★理解Qt的事件模型,掌握其使用的时机 信号与槽.元对象系统.事件模型是Qt机制的核心,如果您想要掌握Qt编程,就需要对它们有比较深入的了解.本章重点介绍了信号与槽的基本概念和用法.元对象系统.Qt的事件模型,以及它们在实际使用过程中应注意的一些问题. Qt对

UVM基础之---------uvm factory机制register

factory机制的一大特点就是根据类的名字来创建类的实例. factory 机制中根据类名来创建类的实例所用到的技术:一是参数化的类,二是静态变量和静态函数.这两者是factory机制实现的根本所在. UVM 中有两大关键类,uvm_object 和 uvm_component.一个 uvm_object 在定义时一般要调用 uvm_object_utils 宏,而一个 uvm_component 在定义时要调用uvm_component_utils宏.factory所有的操作都通过这两个宏来

Spring 的核心机制:依赖注入(控制反转)

一.说到依赖注入(控制反转),先要理解什么是依赖. Spring 把相互协作的关系称为依赖关系.假如 A 组件调用了 B 组件的方法,我们可称A 组件依赖于 B 组件. 二.什么是依赖注入. 在传统的程序设计过程中,通常由调用者来创建被调用者的实例. 在依赖注入的模式下,创建被调用者的工作不再由调用者来完成,因此称为控制反转:创建被调用者实例的工作通常由Spring 容器来完成,然后注入给调用者,因此也称为依赖注入. 三.依赖注入的好处. 依赖注入让 Spring 的 Bean 以被指文件组织在

【java基础】Java反射机制

一.预先需要掌握的知识(java虚拟机)  1)java虚拟机的方法区:  java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区.方法区的主要作用是存储被装载的类 的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class 中的类型信息,将这些信息存储到方法区中.这些信息主要包括: 这个类型的全限定名 这个类型的直接超类的全限定名 这个类型是类类型还是接口类型

云计算平台最核心的五项技术

不知不觉间,一向以高大上形象示人的云计算也开始慢慢为普通人所熟知,那么今天我就在这里分析一下云计算平台最核心的五项技术: 1.云服务器 云服务器提供简单高效,处理能力可弹性伸缩的计算服务,支持国内领先的云计算技术和大规模分布存储技术,使您的系统更稳定.数据更安全.传输更快速.部署更灵活. 功能特点 机型丰富 通过高性能服务器虚拟化为云服务器,提供丰富配置类型虚拟机,极大简化数据存储.数据库搭建.web服务器搭建等工作: 仅需要几分钟,根据CPU.内存.数据存储空间和网络带宽等需求,或根据已经配置

NASNet学习笔记——?? 核心一:延续NAS论文的核心机制使得能够自动产生网络结构; ?? 核心二:采用resnet和Inception重复使用block结构思想; ?? 核心三:利用迁移学习将生成的网络迁移到大数据集上提出一个new search space。

from:https://blog.csdn.net/xjz18298268521/article/details/79079008 NASNet总结 论文:<Learning Transferable Architectures for Scalable Image Recognition> 注 ??先啥都不说,看看论文的实验结果,图1和图2是NASNet与其他主流的网络在ImageNet上测试的结果的对比,图3是NASNet迁移到目标检测任务上的检测结果,从这图瞬间感觉论文的厉害之处了,值

《高性能SQL调优精要与案例解析》一书谈主流关系库SQL调优(SQL TUNING或SQL优化)核心机制之——索引(index)

继<高性能SQL调优精要与案例解析>一书谈SQL调优(SQL TUNING或SQL优化),我们今天就谈谈各主流关系库中,占据SQL调优技术和工作半壁江山的.最重要的核心机制之一——索引(index).我们知道,<高性能SQL调优精要与案例解析>一书中也再三强调索引对SQL调优的重要性,可是上篇文章中也谈到,只看案例和解决问题的具体方法,而不掌握SQL调优的基础知识,是没有用的,我们必须做到知其然,更要知其所以然,才能做到融会贯通,活学活用,进而将SQL调优技术掌握到炉火纯青的地步.