Flask启动流程
Posted Flask学习笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask启动流程相关的知识,希望对你有一定的参考价值。
Flask
启动流程
请结合
Flask
上下文一起阅读:https://wenyan.online/2020/10/19/flask-shang-xia-wen/
Flask
依赖于werkzeug
和jinja
两个核心库,werkzeug
是HTTP
与WSGI
相关的包集合.jinja
主要用于渲染前端模板文件.
werkzeug
实现了WSGI
协议,一个典型的WSGI
类似于:
WSGI
:全称Web Server Gateway Interface
,他不是服务器,Python
模块,框架,API
,只是一种规范,用来描述web server
如何与web application
通信的规范.根据它,可以实现很多的相关框架,比如Flask,Django
WSGI server
主要负责从客户端接受request
,并转发给application
,application
处理完数据后,WSGI server
在把Response
返回给客户端.WSGI application
主要是来处理request
,它可以包含多个中间件.environ
指的是一个包含请求信息的对象.
1.Flask
简单应用
一个简单的
Flask
服务器如下.from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'This is {}'.format(username)
if __name__ == '__main__':
app.run(debug=True)这里从
app.run()
来一步一步的看,它都执行了什么.app.run()
# 定义在 flask/app.py中
class Flask(_PackageBoundObject):
# .....
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
# ....
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
# 可以看到 run() 方法最重要的是使用了 werkzeug.serving.run_simgle()方法
# 查看 run_simple()
# werkzeug.serving.run_simgle()
def run_sinple():
# ...
srv = make_server(
hostname,
port,
application,
threaded,
processes,
request_handler,
passthrough_errors,
ssl_context,
fd=fd,
)
#...
# 可以看到这里主要使用了一个 make_server的方法
# 此方法也定义在 werkzeug.serving.make_server()
def make_server():
# ''''''
if threaded and processes > 1:
raise ValueError("cannot have a multithreaded and multi process server.")
elif threaded:
return ThreadedWSGIServer(
host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
)
elif processes > 1:
return ForkingWSGIServer(
host,
port,
app,
processes,
request_handler,
passthrough_errors,
ssl_context,
fd=fd,
)
else:
return BaseWSGIServer(
host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
)
# 查看 ThreadedWSGIServer,ForkingWSGIServer,BaseWSGIServer
# 这三个方法同样定义在 werkzeug.serving.BaseWSGIServer
class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
pass
class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
pass
class BaseWSGIServer(HTTPServer, object):
pass
# 可以看到最主要的类是 BaseWSGIServer
class BaseWSGIServer(HTTPServer, object):
"""Simple single-threaded, single-process WSGI server."""
multithread = False
multiprocess = False
request_queue_size = LISTEN_QUEUE
def __init__(
self,
host,
port,
app,
handler=None,
passthrough_errors=False,
ssl_context=None,
fd=None,
):
if handler is None:
handler = WSGIRequestHandler
HTTPServer.__init__(self, server_address, handler)
# ....
# 这里使用了最重要的一个类 WSGIRequestHandler
# 这个类也定义在 werkzeug.serving.WSGIRequestHandler
class WSGIRequestHandler(BaseHTTPRequestHandler, object):
# ...
def execute(app):
application_iter = app(environ, start_response)
# ...
try:
execute(self.server.app)
# ...
# 可以看到这个类最主要就是执行了 execute(app) 这个方法,而根据传递 这里的
# app 就是最开始生成的app=Flask(__name__)
# 执行到最后相当于执行 app() 这个函数
# 找到 flask/app.py 下的 Flask.__call__
class Flask:
#...
def wsgi_app(self, environ, start_response):
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)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
# 最终的逻辑定义在 Flask.wsgi_app()中.
2.wsgi_app
Flask
虽然运行,但是并没有真正的去处理请求,当真正有请求时,是调用Flask.wsgi_app()
去处理class Flask:
def request_context(self, environ):
return RequestContext(self, environ)
def wsgi_app(self, environ, start_response):
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)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)ctx = self.request_context(environ)
# 开始就进行了 请求上下文
# 这个方法同样定义在Flask中,引用的是 RequestContext
# 此方法定义在 flask/ctx.py 中
class RequestContext(object):
def __init__(self, app, environ, request=None, session=None):
self.app = app
# 最开始 request 为None
if request is None:
# 定义在 flask/app.py中
# request_class =Request
# 这个Request,就是 flask/wrappers.py 下的Request类
# 这个类主要是用来实现路由的.
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
# 上下文对象的 url_adapter,主要是将请求中的URL和Map实例中的URL
# 进行匹配
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
self._implicit_app_ctx_stack = []
self.preserved = False
self._preserved_exc = None
self._after_request_functions = []
#全局变量g
@property
def g(self):
return _app_ctx_stack.top.g
@g.setter
def g(self, value):
_app_ctx_stack.top.g = value
def copy(self):
return self.__class__(
self.app,
environ=self.request.environ,
request=self.request,
session=self.session,
)
def match_request(self):
try:
result = self.url_adapter.match(return_rule=True)
self.request.url_rule, self.request.view_args = result
except HTTPException as e:
self.request.routing_exception = e
# 最主要的是实现了 push 和 pop方法
# 将请求上下文绑定到当前上下文
def push(self):
# 获取请求上下文,开始是None
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)
# 获取应用上下文,开始为None
app_ctx = _app_ctx_stack.top
# 先创建 application context,并push到堆栈结构
if app_ctx is None or app_ctx.app != self.app:
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()
# self是RequestContext对象,其中包含了请求的所有数据
# push RequestContext 到堆栈结构
# push当前上下文到堆栈结构中
_request_ctx_stack.push(self)
# 这是最重要的方法,保证了_request_ctx_stack中永远指向了当前的上下文
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)
if self.url_adapter is not None:
self.match_request()刚启动
app
,应该还没有生成上下文,也就是上下文为{}
的时候,因为我们并没有去访问网页.from flask import Flask, request
from flask.globals import _app_ctx_stack, _request_ctx_stack
app = Flask(__name__)
# 堆栈结构
print(_app_ctx_stack._local.__storage__)
print(_request_ctx_stack._local.__storage__)
# {}
# {}
@app.route('/')
def index():
username = request.args.get('username')
print(_app_ctx_stack._local.__storage__)
print(_request_ctx_stack._local.__storage__)
return 'This is {}'.format(username)
if __name__ == '__main__':
app.run(debug=True)
3.路由
现在来看一下路由的形成.
@app.route('/')
def index():
username = request.args.get('username')
return 'This is {}'.format(username)这里
route
方法是一个装饰器,具体的实现逻辑在add_url_rule()
# flask/app.py
class Flask():
# ...
def route(self,rule,**options):
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
# add_url_rule
@setupmethod
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
methods = options.pop("methods", None)
# ....
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an "
"existing endpoint function: %s" % endpoint
)
self.view_functions[endpoint] = view_func它主要的作用是将路由添加到
self.url_map
中.通过rule
和endpoint
的对应,可以很方便的访问到网页.以上,当执行完
ctx.push()
后,Local
对象中已经有数据了,接着执行self.full_dispatch_request
,也就是执行视图函数,它的作用是根据请求的url
找到对应的蓝本里面的视图函数,并生成一个response
对象.def full_dispatch_request(self):
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)
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv)
try:
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
return response最后,会清空堆栈结构中的数据,为下一次的上下文做准备.
ctx.auto_pop(error)
# auto_pop中实现了 ResponseContext.pop()方法.
4.发起访问
- END -from flask import Flask, request
from flask.globals import _app_ctx_stack, _request_ctx_stack
app = Flask(__name__)
# 堆栈结构
print(_app_ctx_stack._local.__storage__)
print(_request_ctx_stack._local.__storage__)
# {}
# {}
@app.route('/')
def index():
username = request.args.get('username')
print(_app_ctx_stack._local.__storage__)
print(_request_ctx_stack._local.__storage__)
return 'This is {}'.format(username)
if __name__ == '__main__':
app.run(debug=True)在浏览器中发起访问
http://127.0.0.1:5000/?username=python
{<greenlet.greenlet object at 0x7f0b30a17260>: {'stack': [<flask.ctx.AppContext object at 0x7f0b30a1e210>]}}
{<greenlet.greenlet object at 0x7f0b30a17260>: {'stack': [<RequestContext 'http://127.0.0.1:5000/?username=python' [GET] of app>]}}可以看到其中有一个
RequestContext
对象.当真正发起访问http://127.0.0.1:5000/?username=python
,就会调用Flask.wsgi_app()
,此时调用了RequestContext.push(self)
,也就是当前上下文被push
到了__request_ctx_stack
堆栈结构中.这就是上下文结构.当访问结束后,
ctx.auto_pop(error)
会清空堆栈结构.
以上是关于Flask启动流程的主要内容,如果未能解决你的问题,请参考以下文章