为什么运行Flask开发服务器会运行两次?

176 投票
8 回答
54357 浏览
提问于 2025-04-18 18:33

我正在使用Flask来开发一个网站,在开发过程中,我用下面的文件来运行Flask:

#!/usr/bin/env python
from datetime import datetime
from app import app
import config

if __name__ == '__main__':
    print('################### Restarting @', datetime.utcnow(), '###################')
    app.run(port=4004, debug=config.DEBUG, host='0.0.0.0')

当我启动服务器,或者因为文件更新而自动重启时,它总是会显示打印的内容两次:

################### Restarting @ 2014-08-26 10:51:49.167062 ###################
################### Restarting @ 2014-08-26 10:51:49.607096 ###################

虽然这并不算什么大问题(其他功能都正常),但我只是想知道为什么会这样?有什么想法吗?

8 个回答

2

我正在使用一个插件 - python-dotenv,

我会在我的配置文件 - .flaskenv 中添加以下内容:

FLASK_RUN_RELOAD=False

这样可以避免 Flask 运行两次。

7

从Flask 0.11版本开始,建议你用 flask run 来启动你的应用,而不是用 python application.py。如果用后者,可能会导致你的代码被执行两次。

这里有说明

... 从Flask 0.11开始,推荐使用flask方法。这样做的原因是,由于重载机制的工作方式,可能会出现一些奇怪的副作用(比如某些代码被执行两次...)

10

我也遇到过同样的问题,我是通过把 app.debug 设置为 False 来解决的。把它设置为 True 的话,会导致我的 __name__ == "__main__" 被调用两次。

19

如果你在使用现代的 flask run 命令,那么 app.run 中的选项都不会被使用。要完全禁用自动重载功能,可以加上 --no-reload 这个选项:

FLASK_DEBUG=1 flask run --no-reload

另外,__name__ == '__main__' 这个条件永远不会成立,因为应用程序并不是直接执行的。可以参考 Martijn 的回答 中的思路,只不过不需要 __main__ 这个部分。

if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
    # do something only once, before the reloader

if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    # do something each reload
240

Werkzeug的重载功能会启动一个子进程,这样每当你的代码发生变化时,它就可以重新启动这个进程。Werkzeug是一个库,当你调用 app.run() 时,它为Flask提供了开发服务器。

你可以查看 restart_with_reloader() 函数的代码;你的脚本会通过 subprocess.call() 再次运行。

如果你把 use_reloader 设置为 False,你会发现这个重载的功能消失了,但这样你也会失去自动重载的功能:

app.run(port=4004, debug=config.DEBUG, host='0.0.0.0', use_reloader=False)

你在使用 flask run 命令时,也可以禁用重载功能:

FLASK_DEBUG=1 flask run --no-reload

如果你想检测自己是否在重载的子进程中,可以使用 werkzeug.serving.is_running_from_reloader 函数

from werkzeug.serving import is_running_from_reloader

if is_running_from_reloader():
    print(f"################### Restarting @ {datetime.utcnow()} ###################")

不过,如果你需要设置模块的全局变量,最好使用 @app.before_first_request 装饰器,在一个函数上使用这个装饰器,让这个函数来设置全局变量。这个函数会在每次重载后,当第一个请求到来时被调用一次:

@app.before_first_request
def before_first_request():
    print(f"########### Restarted, first request @ {datetime.utcnow()} ############")

需要注意的是,如果你在一个完整的WSGI服务器中运行这个服务器,而这个服务器使用了分叉或新的子进程来处理请求,那么 before_first_request 的处理函数 可能 会在每个新的子进程中被调用。

撰写回答