Warum läuft der Flask Dev Server zweimal selbst?

106

Ich verwende Flask zum Entwickeln einer Website und während der Entwicklung führe ich flask mit der folgenden Datei aus:

#!/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')

Wenn ich den Server starte oder ihn automatisch neu startet, weil Dateien aktualisiert wurden, wird die Druckzeile immer zweimal angezeigt:

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

Obwohl es nicht wirklich ein Problem ist (der Rest funktioniert wie erwartet), frage ich mich einfach, warum es sich so verhält? Irgendwelche Ideen?

kramer65
quelle

Antworten:

152

Der Werkzeug-Reloader erzeugt einen untergeordneten Prozess, sodass er diesen Prozess jedes Mal neu starten kann, wenn sich Ihr Code ändert. Werkzeug ist die Bibliothek, die Flask beim Aufruf mit dem Entwicklungsserver versorgt app.run().

Siehe restart_with_reloader()Funktionscode ; Ihr Skript wird erneut mit ausgeführt subprocess.call().

Wenn Sie auf einstellen use_reloader, Falsewird das Verhalten verschwinden, aber Sie verlieren auch die Neuladefunktion:

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

Sie können den Reloader auch deaktivieren, wenn Sie den flask runBefehl verwenden:

FLASK_DEBUG=1 flask run --no-reload

Sie können nach der WERKZEUG_RUN_MAINUmgebungsvariablen suchen , wenn Sie erkennen möchten, wann Sie den untergeordneten Prozess neu laden:

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

Wenn Sie jedoch Modul-Globals einrichten müssen, sollten Sie stattdessen den @app.before_first_requestDekorator für eine Funktion verwenden und diese Funktion solche Globals einrichten lassen. Es wird nur einmal nach jedem Neuladen aufgerufen, wenn die erste Anfrage eingeht:

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

Berücksichtigen Sie, dass before_first_requestHandler für jeden neuen Unterprozess aufgerufen werden können , wenn Sie dies auf einem vollständigen WSGI-Server ausführen, der Forking oder neue Unterprozesse zur Verarbeitung von Anforderungen verwendet .

Martijn Pieters
quelle
2
Ah ok. Danke für die Erklärung! Also ist es normales Verhalten? Zumindest gut, dass nichts mit meinem Code falsch ist .. :)
kramer65
1
@ kramer65: es ist ganz normal und erwartetes verhalten. :-)
Martijn Pieters
1
Gibt es eine praktische Möglichkeit, langsamen Initialisierungscode nur einmal auszuführen, um sicherzustellen, dass er auch aufgerufen wird, wenn er unter wsgi ausgeführt wird (dh nicht von app.run), aber nicht auf die erste Anforderung wartet? Ich möchte nicht, dass diese erste Anfrage mit den Initialisierungskosten belastet wird.
Kylotan
1
@Kylotan: Sie müssten die Umgebung inspizieren; Wenn Sie DEBUG nur während der Ausführung in der Entwicklung festlegen, können Sie nach der WERKZEUG_RUN_MAINUmgebungsvariablen suchen und Ihren Code nur ausführen, wenn er DEBUGfalsch ist oder WERKZEUG_RUN_MAINbeispielsweise festgelegt ist. Wird ein bisschen langweilig.
Martijn Pieters
Um dies zu verdeutlichen, dachte ich, dass "Nachladen der Funktionalität" Reaktivität bedeutet (was den gesamten Zweck der Verwendung dashfür mich zunichte machen würde ). Für andere noobswie mich bedeutet dies nur die Funktionalität, mit der das Bearbeiten / Speichern der Datei ein Live-Update auslöst.
Hendy
12

Wenn Sie den modernen flask runBefehl verwenden, wird keine der zu app.runverwendenden Optionen verwendet. Um den Reloader vollständig zu deaktivieren, übergeben Sie --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Auch __name__ == '__main__'wird nie wahr sein, weil die App nicht direkt ausgeführt wird. Verwenden Sie die gleichen Ideen aus Martijns Antwort , außer ohne den __main__Block.

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
Davidismus
quelle
7

Ich hatte das gleiche Problem, und ich es gelöst , indem app.debugan False. Das Einstellen auf führte dazu, Truedass ich __name__ == "__main__"zweimal angerufen wurde.

Carvell Wakeman
quelle
Mein __main__läuft noch zweimal mit beiden app.debug = Falseund app.run_server(debug=False). Sind Sie sicher, dass dies für Sie getan wurde, oder können Sie reproduzierbaren Code veröffentlichen, um es zu versuchen?
Hendy
Das Ändern von app.debug war alles, was ich getan habe, um es für mich zu lösen. Können Sie bestätigen, dass main beim Starten des Kolbenservers nur zweimal ausgeführt wird? Versuchen Sie, ein minimales Arbeitsbeispiel zum Laufen zu bringen, und prüfen Sie, ob das Problem auftritt. Versuchen Sie auch, ein minimales Beispiel auszuführen, das in mehreren Python-Versionen fehlschlägt. Dies war möglicherweise ein Problem. Ich habe mein Projekt seitdem auf Java und SparkJava anstatt auf Python und Flask migriert, daher kann ich mich nicht genau erinnern, was das Problem behoben hat.
Carvell Wakeman
Ich verwende flasküber plotly dashund fand heraus , dass sie vor kurzem die Standard geändert debug Argument übergeben flask. Ich werde vermuten, dass ich mich oben geirrt habe und es vielleicht getan habe app.debug=False(was möglicherweise durch die Standardargumente überschrieben wird run_server) oder es nur ohne Bestehen versucht habe, ohne Trueexplizit wie oben gezeigt einzustellen. Dies funktioniert jetzt für mich richtig (stellen Sie sicher, dass debug=False). Vielen Dank!
Hendy
2

Von Flask 0.11, ist es empfehlenswert , die App mit laufen , flask runstatt python application.py. Wenn Sie Letzteres verwenden, wird Ihr Code möglicherweise zweimal ausgeführt.

Wie hier angegeben :

... ab Kolben 0.11 wird die Kolbenmethode empfohlen. Der Grund dafür ist, dass es aufgrund der Funktionsweise des Reload-Mechanismus einige bizarre Nebenwirkungen gibt (wie das zweimalige Ausführen bestimmter Codes ...).

salsa_man
quelle
0

Einer der möglichen Gründe, warum die Flask-App zweimal selbst ausgeführt wird, ist die Konfiguration der WEB_CONCURRENCYEinstellungen für Heroku. Zum Einstellen können Sie in die Konsole schreiben heroku config:set WEB_CONCURRENCY=1

trojek
quelle
-1

Eine Beobachtung zu Threads

Dies ist besonders ärgerlich, wenn Ihre Anwendung Threads verwendet, da diese beim Start zweimal ausgelöst werden. Soweit ich Singletons ausprobiert habe, kann ich das auch nicht beheben (was überraschend ist). Das Hinzufügen einer anfänglichen Verzögerung von einigen Sekunden vor dem Start Ihres Threads kann das Problem jedoch beheben.

Wenn die App schneller neu startet als vor Ablauf Ihrer Verzögerungszeit, wird der angegebene Thread nach dem Neustart nur einmal erzeugt.

pfabri
quelle
@ Dowvoter: Möchtest du erklären warum?
Pfabri
-1

Ich hatte das gleiche Problem. Ich habe es gelöst, indem ich mein main geändert und use_reloader = False eingefügt habe. Wenn hier jemand nach einer Problemumgehung für dieses Problem sucht, können Sie mit dem folgenden Code beginnen. Die Funktionalität von Codeänderungen, die von der Anwendung automatisch erkannt werden und neu gestartet wird, funktioniert jedoch nicht. Sie müssen Ihre Anwendung nach jeder Bearbeitung im Code manuell stoppen und neu starten.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
LA
quelle