首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
V2EX  ›  Python

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

  •  
  •   KeatingSmith · 2018-07-24 17:21:34 +08:00 · 3624 次点击
    这是一个创建于 500 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 服务器是以何种方式处理请求的呢?比如说是 单进程多线程,多进程多线程还是单进程单线程?

    19 回复  |  直到 2018-07-29 22:34:47 +08:00
        1
    windardyang   2018-07-24 17:32:18 +08:00   ♥ 1
    默认单线程单进程。
    `app.run(threaded=True)` 多进程
    `app.run(processes=4)` 多线程
        2
    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 运行的话,相当于开启了四个进程,那也就是说,四个进程也是单线程模式?

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

    gunicorn 使用按照 wsgi 协议启动 app,app.run 使用的是 werkzeug 的 run_simple 启动 app,你看一下 run_simple 的启动参数或者 flask 源码就知道了。
        4
    ox180   2018-07-24 17:59:04 +08:00   ♥ 1
    问题一:
    由于 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 进程,至于是不是单进程多线程方式,那是另外一个问题了....
        5
    KeatingSmith   2018-07-24 18:11:51 +08:00
    @windardyang

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

    谢谢前辈赐教。


    @ox180

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

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

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

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

    我也是,写好了,记得 mark 一下,谢谢藕
        14
    gnozix   2018-07-25 16:42:35 +08:00
    @gnozix 0.12 和 1.0.2 的默认设置不一样。。0_0
    @KeatingSmith ,对的
        15
    ox180   2018-07-26 08:49:57 +08:00
    @gnozix 如上描述
        16
    gnozix   2018-07-26 09:06:33 +08:00
    @ox180 因为格式原因,我看的我项目的,而我项目还用的 0.12 ,和 1.0.2 的默认设置是不同的。
        17
    windardyang   2018-07-26 14:08:43 +08:00
    @vipppppp 1 楼是太匆忙写反了,Thread 表示线程,Process 表示进程。
        18
    luffysup   2018-07-27 17:48:32 +08:00
    @windardyang 细致
        19
    Nostalgiaaaa   2018-07-29 22:34:47 +08:00
    @KeatingSmith 感觉摊的有点大,写了一部分你先看看吧 别忘了点个 star,这个还会持续更新。https://python-fbw.gitbook.io/flask/
    这次看完 Flask 会看 werkzeug 的一部分代码,之后会再看 Request 的代码,每周都能更新三四次。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2562 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 25ms · UTC 11:36 · PVG 19:36 · LAX 03:36 · JFK 06:36
    ♥ Do have faith in what you're doing.