2.flask 源码解析:应用启动流程
Flask 源码分析完整教程目录:https://www.cnblogs.com/nickchen121/p/14763457.html
一、flask 源码解析:应用启动流程
1.1 WSGI
所有的 python web 框架都要遵循 WSGI 协议
在这里还是要简单回顾一下 WSGI 的核心概念。
WSGI 中有一个非常重要的概念:每个 python web 应用都是一个可调用(callable)的对象。在 flask 中,这个对象就是 app = Flask(__name__)
创建出来的 app
,就是下图中的绿色 Application 部分。要运行 web 应用,必须有 web server,比如我们熟悉的 apache、nginx ,或者 python 中的 gunicorn ,我们下面要讲到的 werkzeug
提供的 WSGIServer
。
Server 和 Application 之间怎么通信,就是 WSGI 的功能。它规定了 app(environ, start_response)
的接口,server 会调用 application,并传给它两个参数:environ
包含了请求的所有信息,start_response
是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。
WSGI application 非常重要的特点是:它是可以嵌套的。换句话说,我可以写个 application,它做的事情就是调用另外一个 application,然后再返回(类似一个 proxy)。一般来说,嵌套的最后一层是业务应用,中间就是 middleware。这样的好处是,可以解耦业务逻辑和其他功能,比如限流、认证、序列化等都实现成不同的中间层,不同的中间层和业务逻辑是不相关的,可以独立维护;而且用户也可以动态地组合不同的中间层来满足不同的需求。
WSGI 的内容就讲这么多,我们来看看 flask 的 hello world 应用:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
这里的 app = Flask(__name__)
就是上面提到的 Application 部分,但是我们并没有看到 Server 的部分,那么它一定是隐藏到 app.run()
内部某个地方了。
1.2 启动流程
应用启动的代码是 app.run()
,这个方法的代码如下:
def run(self, host=None, port=None, debug=None, **options):
"""Runs the application on a local development server."""
from werkzeug.serving import run_simple
# 如果host 和 port 没有指定,设置 host 和 port 的默认值 127.0.0.1 和 5000
if host is None:
host = '127.0.0.1'
if port is None:
server_name = self.config['SERVER_NAME']
if server_name and ':' in server_name:
port = int(server_name.rsplit(':', 1)[1])
else:
port = 5000
# 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数
# 注意第三个参数传进去的是 self,也就是要执行的 web application
try:
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
NOTE:为了阅读方便,我删除了注释和不相干的部分,下面所有的代码都会做类似的处理,不再赘述。
这个方法的内容非常简单:处理一下参数,然后调用 werkzeug
的 run_simple
。需要注意的是:run_simple
的第三个参数是 self
,也就是我们创建的 Flask()
application。因为 WSGI server 不是文章的重点,所以我们就不深入讲解了。现在只需要知道它的功能就行:监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app
去执行处理的逻辑。对应的执行逻辑在 werkzeug.serving:WSGIRequestHandler
的 run_wsgi
中有这么一段代码:
def execute(app):
application_iter = app(environ, start_response)
try:
for data in application_iter:
write(data)
if not headers_sent:
write(b'')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None
可以看到 application_iter = app(environ, start_response)
就是调用代码获取结果的地方。
要调用 app
实例,那么它就需要定义了 __call__
方法,我们找到 flask.app:Flask
对应的内容:
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
"""The actual WSGI application.
"""
# 创建请求上下文,并把它压栈。这个在后面会详细解释
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
# 正确的请求处理路径,会通过路由找到对应的处理函数
response = self.full_dispatch_request()
except Exception as e:
# 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常
error = e
response = self.handle_exception(e)
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 不管处理是否发生异常,都需要把栈中的请求 pop 出来
ctx.auto_pop(error)
上面这段代码只有一个目的:找到处理函数,然后调用它。除了异常处理之外,我们还看到了 context
相关的内容(开始有 ctx.push()
,最后有 ctx.auto_pop()
的逻辑),它并不影响我们的理解,现在可以先不用管,后面会有一篇文章专门介绍。
继续往后看,full_dsipatch_request
的代码如下:
def full_dispatch_request(self):
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
"""
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
这段代码最核心的内容是 dispatch_request
,加上请求的 hooks 处理和错误处理的内容。
NOTE:self.dispatch_request()
返回的是处理函数的返回结果(比如 hello world 例子中返回的字符串),finalize_request
会把它转换成 Response
对象。
在 dispatch_request
之前我们看到 preprocess_request
,之后看到 finalize_request
,它们里面包括了请求处理之前和处理之后的很多 hooks 。这些 hooks 包括:
- 第一次请求处理之前的 hook 函数,通过
before_first_request
定义 - 每个请求处理之前的 hook 函数,通过
before_request
定义 - 每个请求正常处理之后的 hook 函数,通过
after_request
定义 - 不管请求是否异常都要执行的
teardown_request
hook 函数
dispatch_request
要做的就是找到我们的处理函数,并返回调用的结果,也就是路由的过程。我们下一篇文章来讲!
2.flask 源码解析:应用启动流程的更多相关文章
- Python Web Flask源码解读(一)——启动流程
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- Flume-ng源码解析之启动流程
今天我们通过阅读Flume-NG的源码来看看Flume的整个启动流程,废话不多说,翠花,上源码!! 1 主类也是启动类 在这里我贴出Application中跟启动有关的方法,其他你们可以自己看源码,毕 ...
- SpringMVC源码解析-DispatcherServlet启动流程和初始化
在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象. 先从Dis ...
- flask源码解析之上下文为什么用栈
楔子 我在之前的文章<flask源码解析之上下文>中对flask上下文流程进行了详细的说明,但是在学习的过程中我一直在思考flask上下文中为什么要使用栈完成对请求上下文和应用上下文的入栈 ...
- Netty源码解析—客户端启动
Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...
- Sentinel源码解析一(流程总览)
引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...
- flask源码解析之上下文
引入 对于flask而言,其请求过程与django有着截然不同的流程.在django中是将请求一步步封装最终传入视图函数的参数中,但是在flask中,视图函数中并没有请求参数,而是将请求通过上下文机制 ...
- Thinkphp6源码分析之解析,Thinkphp6路由,Thinkphp6路由源码解析,Thinkphp6请求流程解析,Thinkphp6源码
Thinkphp6源码解析之分析 路由篇-请求流程 0x00 前言: 第一次写这么长的博客,所以可能排版啊,分析啊,什么的可能会比较乱.但是我大致的流程已经觉得是说的够清楚了.几乎是每行源码上都有注释 ...
- Flask源码解析:Flask应用执行流程及原理
WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...
- 【原创】angularjs1.3.0源码解析之执行流程
Angular执行流程 前言 发现最近angularjs在我厂的应用变得很广泛,下周刚好也有个angular项目要着手开始做,所以先做了下功课,从源代码开始入手会更深刻点,可能讲的没那么细,侧重点在于 ...
随机推荐
- 【Java】【常用类】Calendar 日历类
Calendar 日历类,我居然念错发音,来,好好看下音标 ['kælɪndə] 卡琳达 public class DateTest { public static void main(Strin ...
- 【Docker】04 部署MySQL
从官网上拉取最新MySQL镜像: docker pull mysql:latest 创建并运行一个MySQL镜像的容器 docker run -itd --name mysql-test -p 330 ...
- 全网最适合入门的面向对象编程教程:30 Python的内置数据类型-object根类
全网最适合入门的面向对象编程教程:30 Python 的内置数据类型-object 根类 摘要: 在 Python 中,所有的类都直接或间接继承自一个根类,这个根类是Object.Object类是 P ...
- 2024年世界体育界的第一大丑闻:利昂内尔·梅西 (The biggest scandal in the world of sports in 2024: Unethical player - Lionel Messi.)
无德球员,梅西亲日辱华,不顾球迷感受,拒绝在中国的比赛中上场,并以所谓的伤病为借口,却在3天后的日本比赛中完全恢复如初,并进行了30分钟的高强度的对抗比赛并射门,可以说梅西的这一行径就是对中国亿万百姓 ...
- pyqt编写的走迷宫游戏环境,python语言,exe文件可以成功运行Window10系统上
PS. 要注意,这个项目使用源码安装的方式无法成功运行,显示报错,尝试过多个python版本和pyqt的版本,估计是长期没有维护的原因,因此源码是无法运行的,但是exe的可执行文件(打包后的)是可以成 ...
- OpenALMusicPlayer.cpp:164:22: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
编译时报错: OpenALMusicPlayer.cpp:164:22: error: invalid conversion from 'char' to 'const char*' [-fpermi ...
- vscode 设置窗口菜单栏显示字体大小
最近换了一块大些的显示屏,发现vscode的窗口字体有些小了,不是很方便,于是研究了一下如何设置vscode的窗口字体大小. 需要注意的是这里的设置是对窗口字体的而不是编辑器的字体. 1 . 通过主 ...
- 高级工程师面试大全- java基础篇
1.什么是java虚拟机 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功 ...
- win7安装snmp服务
一.安装SNMP Win7操作系统默认情况下是不安装SNMP服务的,今天讲解一下在Win7操作系统下安装SNMP,具体安装步骤如下: 打开控制面板--卸载程序 WIN7操作系统下安装SNMP的步骤如下 ...
- 基于python的文字转图片工具
地址 https://hub.docker.com/r/rainsccc/strtoimg 拉取镜像后,可以启动一个容器来运行该应用程序.以下命令会启动容器并将其端口映射到主机上: docker ru ...