Flask 自带的服务器是如何处理请求的?Python 中其工作原理是什么?
from flask import Flask
app = Flask(name)
@app.route(’/’)
def index():
return ‘Hello Flask’
if name == ‘main’:
app.run()
假设现在有这么一个最简单的 Flask 代码,Flask 会根据 Request 产生相对应的 Response。
那么,Flask 自带的 WSGI 服务器是以何种方式处理请求的呢?比如说是 单进程多线程,多进程多线程还是单进程单线程?
Flask 自带的服务器是如何处理请求的?Python 中其工作原理是什么?
Flask自带的开发服务器(Werkzeug)处理请求的核心流程是这样的:
首先,当你在代码里执行app.run()时,Werkzeug会启动一个单线程的WSGI服务器。这个服务器在一个循环里监听你指定的端口(默认5000)。
当有HTTP请求进来时,服务器主循环会接收到原始的socket连接。Werkzeug的make_server会创建一个WSGIRequestHandler实例来处理这个连接。这个handler会把原始的HTTP请求数据解析成符合WSGI规范的环境字典(environ)。
关键的一步是调用Flask应用对象本身——因为Flask应用实现了WSGI接口的__call__方法。Werkzeug服务器会执行app(environ, start_response),这触发了Flask的整个请求处理流程。
在Flask内部,它会根据environ中的PATH和METHOD,通过URL映射找到对应的视图函数。然后创建请求上下文,推入到_request_ctx_stack中,这样你才能在视图函数里访问request和session这些对象。
找到视图函数后,Flask执行它,拿到返回值。如果是字符串,就包装成响应对象;如果已经是响应对象,就直接使用。最后,通过start_response回调函数把状态码和响应头发回给Werkzeug服务器,服务器再把响应体发送给客户端。
整个过程是同步阻塞的——一个请求处理完才会处理下一个。这就是为什么开发服务器不适合生产环境,它只能同时处理一个请求。
简单说,就是Werkzeug负责网络IO和WSGI协议,Flask负责路由和业务逻辑。
建议:想深入了解可以看看Werkzeug和Flask的源码,特别是BaseWSGIServer和Flask.__call__方法。
如果是这样的话,我进行测试过。代码如下:python<br>.route('/')<br>def index():<br> time.sleep(10)<br> return 'Hello Flask'<br>
假如是默认是单进程单线程,我开启了 10 个网页,是不是第 10 个网页应该在 100s 后才能接收到 Response,但是实际测试的时候,并不是?shell<br>$ gunicorn -w 4 xxx:app<br>
使用 Gunicorn 运行的话,相当于开启了四个进程,那也就是说,四个进程也是单线程模式?
我找了很多资料,也没有找到答案,请前辈赐教。
问题一:
由于 Flask 代码中默认启用了多线程,如下:python<br> # 大致在 936 行<br> options.setdefault('use_reloader', self.debug)<br> options.setdefault('use_debugger', self.debug)<br> options.setdefault('threaded', True) # here<br><br> cli.show_server_banner(self.env, self.debug, <a target="_blank" href="http://self.name" rel="nofollow noopener">self.name</a>, False)<br><br> from werkzeug.serving import run_simple<br><br> try:<br> run_simple(host, port, self, **options) # 此处启动 server<br> finally:<br> # reset the first request information if the development server<br> # reset normally. This makes it possible to restart the server<br> # without reloader and that stuff from an interactive shell.<br> self._got_first_request = False<br>
然后再往下走,此处忽略,然后最终到达:python<br>def make_server(host=None, port=None, app=None, threaded=False, processes=1,<br> request_handler=None, passthrough_errors=False,<br> ssl_context=None, fd=None):<br> """Create a new server instance that is either threaded, or forks<br> or just processes one request after another.<br> """<br> if threaded and processes > 1:<br> raise ValueError("cannot have a multithreaded and "<br> "multi process server.")<br> elif threaded:<br> return ThreadedWSGIServer(host, port, app, request_handler,<br> passthrough_errors, ssl_context, fd=fd)<br> elif processes > 1:<br> return ForkingWSGIServer(host, port, app, processes, request_handler,<br> passthrough_errors, ssl_context, fd=fd)<br> else:<br> return BaseWSGIServer(host, port, app, request_handler,<br> passthrough_errors, ssl_context, fd=fd)<br>
所以默认使用的是多线程方式,所以解决了问题 1
问题二:
-w 表示启动多少个 worker,简单来讲就是启动几个 flask app 副本,一种简单实现方式:python<br><br># 伪代码<br>def start(app, worker=4, *args, **kwargs):<br> <br> workers = [Process(target=app, args=()) for _ in range(worker))<br> for _worker in workers:<br> _worker.start()<br># 如果你看 sanic,其实也是这个实现方式<br>
所以关于 gunicorn,我的理解就是启动-w 个 flask 程序,如你描述那就是 4 个 flask 进程,至于是不是单进程多线程方式,那是另外一个问题了…
是我测试的方法问题,确实是单进程单线程。
谢谢前辈赐教。
确实,我撇脚的英文看了下文档,-w x 就是启动了 x 个相同的 Flask 应用。
谢谢前辈赐教。
不要用浏览器测并发
浏览器尤其 chrome 会限制对同一个域名的连接… 刚开始学 python 的时候调了很久很久不知道为什么 gevent 开的 flask 就是没法同时打开多个网页… 结果用脚本测并发完全正常
就因为上面的问题, 用 chrome 多 tab 没法对同一个网站提高并发, 最后我是启动了多个 chrome user 才解开的, 求更好更优雅的操作 chrome 方式… cdp 爬虫还有很多小细节等待发现
我测试的方式是通过开启多喝 Chromr 无痕模式。
源码看下来也是单线程啊,怎么是多线程呢?
你 1l 的回答多线程多进程反了吧?
还是我一直理解错误?
先马个,最近在读 flask 源码,读好了写文档 @楼主
源码是通过 werkzeug 的 run_simple 启动服务器的,设置了默认的 options[‘thread’] 为 True, 不知道我的理解对不对。
我也是,写好了,记得 mark 一下,谢谢藕
0.12 和 1.0.2 的默认设置不一样。。0_0
,对的
如上描述
因为格式原因,我看的我项目的,而我项目还用的 0.12 ,和 1.0.2 的默认设置是不同的。
1 楼是太匆忙写反了,Thread 表示线程,Process 表示进程。
细致
感觉摊的有点大,写了一部分你先看看吧 别忘了点个 star,这个还会持续更新。https://python-fbw.gitbook.io/flask/
这次看完 Flask 会看 werkzeug 的一部分代码,之后会再看 Request 的代码,每周都能更新三四次。


