Flask開発サーバーを実行すると、なぜそれ自体が2回実行されるのですか?


106

私はウェブサイトの開発に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')

サーバーを起動したとき、またはファイルが更新されたためにサーバーが自動再起動したときは、常に印刷行が2回表示されます。

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

それは実際には問題ではありませんが(残りは期待どおりに動作します)、なぜこのように動作するのか疑問に思いますか?何か案は?

回答:


152

Werkzeugリローダーは子プロセスを生成し、コードが変更されるたびにそのプロセスを再起動できるようにします。Werkzeugは、Flaskに呼び出し時に開発サーバーを提供するライブラリですapp.run()

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_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 '26

1
@ kramer65:それは完全に正常であり、予想される動作です。:-)
Martijn Pieters

1
遅い初期化コードを一度だけ実行する実用的な方法はありapp.runますか?これは、wsgi(つまりからではなく)で実行されているときにも呼び出され、最初のリクエストを待機しないことを保証しますか?最初のリクエストで初期化コストを負担したくありません。
カイロタン2015年

1
@キロタン:環境を検査する必要があります。開発中に実行するときにDEBUGのみを設定WERKZEUG_RUN_MAINすると、環境変数を検索して、たとえば、DEBUGfalseまたはWERKZEUG_RUN_MAINが設定されている場合にのみコードを実行できます。少し面倒になります。
Martijn Pieters

明確にするために、私は「リロード機能」は反応性を意味すると思っていました(それはdash私にとっての使用の全体的な目的を無効にするでしょう)。noobs私のような他の人にとって、これはファイルの編集/保存がライブ更新をトリガーする機能のみを意味します。
Hendy

12

modern 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)。あなたはそれがあなたのためにそれをしたことを確信していますか、またはあなたが試すためにいくつかの再現可能なコードを投稿できますか?
Hendy

app.debugを変更するだけで解決しました。フラスコサーバーが起動されたときにメインが2回しか実行されていないことを確認できますか?最小限の動作するサンプルを実行してみて、問題が発生するかどうかを確認してください。また、複数のpythonバージョンで失敗する最小限の例を実行してみてください。これは問題だった可能性があります。私はそれ以来、自分のプロジェクトをpythonやフラスコではなくJavaとSparkJavaに移行したので、何が問題を修正したのか正確に覚えていません。
Carvell Wakeman 2017

私はflaskvia を使用していますが、plotly dash最近渡されたデフォルトの debug引数が変更されていることがわかりましたflask。上記のように明示的に設定せずに、上で間違えたのではないかと推測しapp.debug=False(おそらく、デフォルトのargs toによってオーバーライドされる可能性がありますrun_server)、またはを渡さずに試してみましたTrue。これは私にとっては正しく機能しています(確認することdebug=False)。ありがとう!
Hendy

2

Flask 0.11以降、flask runではなくでアプリを実行することをお勧めしますpython application.py。後者を使用すると、コードが2回実行される可能性があります。

ここで述べたように

... Flask 0.11以降、フラスコ法が推奨されます。この理由は、リロードメカニズムの動作方法により、奇妙な副作用(特定のコードを2回実行するなど)があるためです。


0

Flaskアプリが2度実行される理由の1つは、WEB_CONCURRENCYHeroku での設定の構成です。1つに設定するには、コンソールで書くことができます heroku config:set WEB_CONCURRENCY=1


-1

スレッドに関する観察

アプリケーションがスレッドを使用する場合、起動時にスレッドが2回起動されるため、これは特に厄介です。私がシングルトンを試した限り、これも改善されません(これは驚くべきことです)。ただし、スレッドが開始する前に数秒の初期遅延を追加すると、問題を解決できます。

アプリが以前よりも速く再起動した場合、遅延期間が終了すると、再起動後に特定のスレッドが1回だけ生成されます。


@Dowvoter:理由を説明してください。
pfabri

-1

同じ問題がありました。私は私のメインを変更し、それにuse_reloader = Falseを挿入することで解決しました。ボディがこの問題の回避策を探している場合、以下のコードで開始できますが、コードの変更機能が自動的に検出され、アプリケーションを再起動しても機能しません。コードで編集するたびに、アプリケーションを手動で停止して再起動する必要があります。

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.