为什么运行Flask开发服务器会运行两次?
我正在使用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 个回答
我正在使用一个插件 - python-dotenv,
我会在我的配置文件 - .flaskenv 中添加以下内容:
FLASK_RUN_RELOAD=False
这样可以避免 Flask 运行两次。
从Flask 0.11版本开始,建议你用 flask run
来启动你的应用,而不是用 python application.py
。如果用后者,可能会导致你的代码被执行两次。
... 从Flask 0.11开始,推荐使用flask方法。这样做的原因是,由于重载机制的工作方式,可能会出现一些奇怪的副作用(比如某些代码被执行两次...)
我也遇到过同样的问题,我是通过把 app.debug
设置为 False
来解决的。把它设置为 True
的话,会导致我的 __name__ == "__main__"
被调用两次。
如果你在使用现代的 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
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
的处理函数 可能 会在每个新的子进程中被调用。