Flask 自带的服务器是怎样处理请求的?

2018-07-24 17:21:34 +08:00
 KeatingSmith
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 服务器是以何种方式处理请求的呢?比如说是 单进程多线程,多进程多线程还是单进程单线程?

5589 次点击
所在节点    Python
19 条回复
windardyang
2018-07-24 17:32:18 +08:00
默认单线程单进程。
`app.run(threaded=True)` 多进程
`app.run(processes=4)` 多线程
KeatingSmith
2018-07-24 17:43:33 +08:00
@windardyang

如果是这样的话,我进行测试过。代码如下:

```python
@app.route('/')
def index():
time.sleep(10)
return 'Hello Flask'
```

假如是默认是单进程单线程,我开启了 10 个网页,是不是第 10 个网页应该在 100s 后才能接收到 Response,但是实际测试的时候,并不是?

```shell
$ gunicorn -w 4 xxx:app
```

使用 Gunicorn 运行的话,相当于开启了四个进程,那也就是说,四个进程也是单线程模式?

我找了很多资料,也没有找到答案,请前辈赐教。
windardyang
2018-07-24 17:51:43 +08:00
1. 不是 100s 后才接收到的话,那你是多久后接收到的? 99s ?
2. gunicorn 起了 4 个 worker,每个 worker 单独运行 app,与你的 app.run 没有关系,这相当于四个进程,每个 app 单线程单进程。

gunicorn 使用按照 wsgi 协议启动 app,app.run 使用的是 werkzeug 的 run_simple 启动 app,你看一下 run_simple 的启动参数或者 flask 源码就知道了。
ox180
2018-07-24 17:59:04 +08:00
问题一:
由于 Flask 代码中默认启用了多线程,如下:

```python
# 大致在 936 行
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
options.setdefault('threaded', True) # here

cli.show_server_banner(self.env, self.debug, self.name, False)

from werkzeug.serving import run_simple

try:
run_simple(host, port, self, **options) # 此处启动 server
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
```

然后再往下走,此处忽略,然后最终到达:

```python
def make_server(host=None, port=None, app=None, threaded=False, processes=1,
request_handler=None, passthrough_errors=False,
ssl_context=None, fd=None):
"""Create a new server instance that is either threaded, or forks
or just processes one request after another.
"""
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)
```
所以默认使用的是多线程方式,所以解决了问题 1



问题二:


-w 表示启动多少个 worker,简单来讲就是启动几个 flask app 副本,一种简单实现方式:


```python

# 伪代码
def start(app, worker=4, *args, **kwargs):

workers = [Process(target=app, args=()) for _ in range(worker))
for _worker in workers:
_worker.start()
# 如果你看 sanic,其实也是这个实现方式
```

所以关于 gunicorn,我的理解就是启动-w 个 flask 程序,如你描述那就是 4 个 flask 进程,至于是不是单进程多线程方式,那是另外一个问题了....
KeatingSmith
2018-07-24 18:11:51 +08:00
@windardyang

是我测试的方法问题,确实是单进程单线程。

谢谢前辈赐教。


@ox180

确实,我撇脚的英文看了下文档,`-w x` 就是启动了 x 个相同的 Flask 应用。

谢谢前辈赐教。
ClericPy
2018-07-24 19:08:53 +08:00
不要用浏览器测并发
浏览器尤其 chrome 会限制对同一个域名的连接... 刚开始学 python 的时候调了很久很久不知道为什么 gevent 开的 flask 就是没法同时打开多个网页... 结果用脚本测并发完全正常
ClericPy
2018-07-24 19:10:17 +08:00
就因为上面的问题, 用 chrome 多 tab 没法对同一个网站提高并发, 最后我是启动了多个 chrome user 才解开的, 求更好更优雅的操作 chrome 方式... cdp 爬虫还有很多小细节等待发现
KeatingSmith
2018-07-24 20:43:36 +08:00
@ClericPy

我测试的方式是通过开启多喝 Chromr 无痕模式。
gnozix
2018-07-25 09:44:31 +08:00
@ox180 源码看下来也是单线程啊,怎么是多线程呢?
vipppppp
2018-07-25 09:51:37 +08:00
@windardyang
你 1l 的回答多线程多进程反了吧?
还是我一直理解错误?
Nostalgiaaaa
2018-07-25 09:55:34 +08:00
先马个,最近在读 flask 源码,读好了写文档 @楼主
KeatingSmith
2018-07-25 14:11:16 +08:00
@gnozix

源码是通过 werkzeug 的 run_simple 启动服务器的,设置了默认的 options['thread'] 为 True, 不知道我的理解对不对。
KeatingSmith
2018-07-25 14:11:37 +08:00
@Nostalgiaaaa

我也是,写好了,记得 mark 一下,谢谢藕
gnozix
2018-07-25 16:42:35 +08:00
@gnozix 0.12 和 1.0.2 的默认设置不一样。。0_0
@KeatingSmith ,对的
ox180
2018-07-26 08:49:57 +08:00
@gnozix 如上描述
gnozix
2018-07-26 09:06:33 +08:00
@ox180 因为格式原因,我看的我项目的,而我项目还用的 0.12 ,和 1.0.2 的默认设置是不同的。
windardyang
2018-07-26 14:08:43 +08:00
@vipppppp 1 楼是太匆忙写反了,Thread 表示线程,Process 表示进程。
luffysup
2018-07-27 17:48:32 +08:00
@windardyang 细致
Nostalgiaaaa
2018-07-29 22:34:47 +08:00
@KeatingSmith 感觉摊的有点大,写了一部分你先看看吧 别忘了点个 star,这个还会持续更新。https://python-fbw.gitbook.io/flask/
这次看完 Flask 会看 werkzeug 的一部分代码,之后会再看 Request 的代码,每周都能更新三四次。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/473748

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX