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


106

我正在使用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 ###################

尽管这并不是真正的问题(其余部分都可以正常工作),但我只是想知道为什么它会这样?有任何想法吗?

Answers:


152

Werkzeug重装程序会生成一个子进程,以便每次代码更改时都可以重新启动该进程。Werkzeug是在您调用时为Flask提供开发服务器的库app.run()

参见restart_with_reloader()功能代码 ; 您的脚本将再次使用运行subprocess.call()

如果设置use_reloaderFalse,则行为会消失,但同时也会丢失重新加载功能:

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_RUN_MAIN如果要检测何时处于重装子进程中,可以查找环境变量:

import os
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    print '################### Restarting @ {} ###################'.format(
        datetime.utcnow())

但是,如果需要设置模块全局变量,则应该在函数上使用@app.before_first_request装饰器,并让该函数设置此类全局变量。当第一个请求进入时,它将在每次重新加载后被调用一次:

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

请考虑一下,如果您在使用分叉或新子流程来处理请求的全面WSGI服务器中运行此服务器,则可能会为每个新子流程调用before_first_request处理程序。


2
喔好吧。感谢您的解释!那么它被认为是正常行为吗?至少很好,我的代码没什么问题.. :)
kramer65

1
@ kramer65:这完全是正常的和预期的行为。:-)
的Martijn Pieters的

1
有没有一种实用的方法可以只运行一次缓慢的初始化代码,以确保在wsgi下运行时也可以调用该初始化代码(即,不是from app.run),而不必等待第一个请求?我不希望第一个请求负担初始化费用。
Kylotan 2015年

1
@Kylotan:您必须检查环境;例如,如果仅在开发中运行时设置DEBUG,则可以查找WERKZEUG_RUN_MAIN环境变量,并且仅在DEBUG为false或WERKZEUG_RUN_MAIN设置时运行代码。有点乏味。
马丁·彼得斯

为了澄清起见,我认为“重载功能”意味着反应性(这将使dash我无法使用它的全部目的)。对于noobs像我这样的其他人,这仅意味着编辑/保存文件会触发实时更新的功能。
亨迪

12

如果使用的是现代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

7

我遇到了同样的问题,并通过设置app.debug为解决了False。设置True为导致我__name__ == "__main__"被叫了两次。


__main__仍然有两种运行两次app.debug = Falseapp.run_server(debug=False)。您确定这样做是为您完成的,还是可以发布一些可重现的代码进行尝试?
亨迪

更改app.debug是我为我解决的全部工作。您能否确定在启动Flask服务器时main仅运行两次?尝试运行一个最低限度的工作示例,看看是否出现问题。另外,请尝试运行一个在多个python版本中均失败的最小示例,这可能是一个问题。此后,我将项目迁移到Java和SparkJava而不是python和flask,所以我不记得是什么确切地解决了该问题。
Carvell Wakeman

我正在使用flaskvia plotly dash,发现他们最近更改了 debug传递给的默认参数flask。我猜想我在上面可能弄错了app.debug=False,也许是做错了(也许被默认的args覆盖了run_server),或者只是尝试不通过True,没有如上所示进行显式设置。现在,这对我来说是正确的(确保debug=False)。谢谢!
亨迪

2

从Flask 0.11开始,建议使用flask run而不是使用python application.py。使用后者可能会导致您的代码运行两次。

如此处所述

...从Flask 0.11开始,建议使用长瓶法。这样做的原因是,由于重载机制的工作方式,因此会有一些奇怪的副作用(例如两次执行某些代码...)


0

Flask应用程序本身运行两次的可能原因之一是WEB_CONCURRENCY在Heroku上进行的配置配置。要设置为一个,可以在控制台中编写 heroku config:set WEB_CONCURRENCY=1


-1

关于线程的观察

当您的应用程序使用线程时,这尤其令人讨厌,因为它们将在启动时被激发两次。就我尝试过的单身人士而言,也没有补救措施(这很令人惊讶)。但是,在线程启动之前添加几秒钟的初始延迟可以解决此问题。

如果应用重新启动的时间比延迟时间结束之前的重新启动快,则在重新启动后,仅产生一次给定线程。


@Dowvoter:关心解释为什么?
pfabri

-1

我遇到过同样的问题。我通过修改main并在其中插入use_reloader = False来解决它。如果有人在寻找解决此问题的方法,则下面的代码将帮助您入门,但是您将无法自动检测到代码更改并重新启动应用程序,因此该功能将不起作用。每次编辑代码后,您都必须手动停止并重新启动应用程序。

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.