Flask 源码解析:整体流程梳理
创建一个 Flask 实例 app 并启动。
1
2
3
4
5
6
7
8
9
10
11from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
return "index"
if __name__ == '__main__':
app.run()将
url
和view_func
通过app.add_url_rule()
或@app.route()
的方式维护到 app 的url_map
和view_functions
属性中。url_map
包含了路由逻辑,view_functions
存储了对应的逻辑函数,二者通过 endpoint 相关联。
app.run()
中可以接受很多参数,其中threaded
和processes
,用于开启线程支持和进程支持。
app.run()
最终调用了 wsgi 的run_simple()
函数。run_simple
共有三个参数:environ
:为初步处理的 request 请求start_response
:为回调函数Response
:的实例化对象,也是具体的 request 入口,负责具体的逻辑,不同的框架其实是第三个参数不同。
run_simple()
做了两件事:一件:
srv = make_server()
,创建服务器。1
2
3
4
5
6
7
8
9
10
11
12def make_server(host=None, port=None, app=None, threaded=False, processes=1,
request_handler=None, passthrough_errors=False,
ssl_context=None, fd=None):
"""
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)从
make_server()
中可知,Flask 提供了三种 server,每个 server 都实现了process_request()
:ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer)
,多线程1
2
3
4
5
6
7
8
9
10
11
12
13class ThreadingMixIn:
def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
def process_request(self, request, client_address):
t = threading.Thread(target = self.process_request_thread, args = (request, client_address))
t.daemon = self.daemon_threads
t.start()ForkingWSGIServer(ForkingMixIn, BaseWSGIServer)
,多进程1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class ForkingMixIn:
def process_request(self, request, client_address):
"""Fork a new subprocess to process the request."""
pid = os.fork() # 创建一个进程,下面的代码会同时在父进程和子进程中执行。
if pid:
# Parent process
if self.active_children is None:
self.active_children = set()
self.active_children.add(pid)
self.close_request(request)
return
else:
# Child process.
# This must never return, hence os._exit()!
status = 1
try:
self.finish_request(request, client_address)
status = 0
except Exception:
self.handle_error(request, client_address)
finally:
try:
self.shutdown_request(request)
finally:
os._exit(status)BaseWSGIServer(HTTPServer)
,单进程单线程。默认情况下是 BaseWSGIServer
想要配置多线程或者多进程,则需要设置 threaded 或 processes 这两个参数,而这两个参数是从
app.run()
中传递过来的app.run(**options) ---> run_simple(threaded,processes) ---> make_server(threaded,processes)
一件:
srv.server_forver()
,一直在监听端口,等待 request 连接。serve_forever()
最终是调用的BaseServer.serve_forever()
,BaseServer.serve_forever()
会使用 selector 模块监听相应的端口1
2
3
4
5
6
7class BaseServer:
def serve_forever(self, poll_interval=0.5):
while not self.__shutdown_request:
ready = selector.select(poll_interval)
if ready:
# 接收到具体请求后 进行处理
self._handle_request_noblock()当有请求到来时:
_handle_request_noblock()
调用process_request()
调用finish_request()
实例化RequestHandlerClass()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28class BaseServer:
def _handle_request_noblock(self):
try:
request, client_address = self.get_request()
except OSError:
return
if self.verify_request(request, client_address):
try:
# 接收到具体请求后 进行处理
self.process_request(request, client_address)
except Exception:
self.handle_error(request, client_address)
self.shutdown_request(request)
except:
self.shutdown_request(request)
raise
else:
self.shutdown_request(request)
def process_request(self, request, client_address):
# 接收到具体请求后 进行处理
self.finish_request(request, client_address)
self.shutdown_request(request)
def finish_request(self, request, client_address):
# 每次都实例化了 WSGIRequestHandler 进行请求处理
# 注意第三个参数把当前的 server 实例传入
self.RequestHandlerClass(request, client_address, self)RequestHandlerClass
其实是WSGIRequestHandler
,最终请求的处理是在WSGIRequestHandler
实例化过程中处理的。最终会将run_simple()
中的第三个参数(也就是app)加括号执行,并传入参数(environ, start_response)
。
从上可知,当一个请求到来时,会调用
Flask.__call__(environ, start_response)
。__call__
方法调用wsgi_app()
,wsgi_app()
具体流程如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class Flask(_PackageBoundObject):
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
# 这个文件是 Flask 的整个执行流程的入口
def wsgi_app(self, environ, start_response):
"""
1、获取 environ 并再次封装。返回 request、session(此时为空)、app(其实就是 self)
ctx.request = Request(environ)
ctx.session = None
ctx.app=app
"""
ctx = self.request_context(environ) # 实际执行ctx = RequestContext(self, environ)
error = None
try:
try:
# 2、把 app_ctx,request_ctx 放到“某个神奇”的地方。
# 对 session 的操作,执行SecureCookieSessionInterface.open_session(),去 cookie 中获取 session 的值,反序列化解密之后给ctx.session 重新赋值(默认 session 的值存在 cookie 中)。
ctx.push()
# 3、执行视图函数,去“某个神奇”的地方获取 session
# 然后加密,序列化,写入cookie,然后返回
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
# 4、此处调用 flask.Response.__call__ 方法
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 5、“某个神奇”的地方位置清空 (请求结束)
ctx.auto_pop(error)将 request 和 session 相关封装到
ctx = RequestContext
对象中,此时 session 为空,request 为二次封装;将 app 和 g 封装到app_ctx = AppContext
对象中。去 cookie 中获取 session 的值,反序列化解密之后给
ctx.session
重新赋值(默认情况下 session 的值存在 cookie 中);通过 LocalStack 对象将 ctx、app_ctx 封装到 Local 对象中。根据 environ 路由到具体的
view_function
执行,并返回 response。执行一些善后工作。
- 将
view_function()
的返回转换为flask.Response
对象。 - 对 session 进行加密,然后 dumps 编程字符串,保存到 cookie 中(默认情况下)。
- 以 WSGI 的方式(其实就是
flask.Response
对象)将 status、headers、response body 返回给 WSGI Server。
- 将
最后,WSGI Server 再将 WSGI 格式的数据转换为 HTTP 格式的数据返回给客户端