Perché l'esecuzione del server di sviluppo Flask viene eseguita due volte?


107

Sto usando Flask per sviluppare un sito web e durante lo sviluppo eseguo flask usando il seguente file:

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

Quando avvio il server, o quando si riavvia automaticamente perché i file sono stati aggiornati, mostra sempre due volte la riga di stampa:

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

Sebbene non sia proprio un problema (il resto funziona come previsto), mi chiedo semplicemente perché si comporta in questo modo? Qualche idea?

Risposte:


153

Il ricaricatore Werkzeug genera un processo figlio in modo che possa riavviarlo ogni volta che il codice cambia. Werkzeug è la libreria che fornisce a Flask il server di sviluppo quando si chiama app.run().

Vedere il restart_with_reloader()codice della funzione ; lo script viene eseguito di nuovo con subprocess.call().

Se imposti use_reloadersu Falsevedrai il comportamento scomparire, ma perderai anche la funzionalità di ricaricamento:

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

Puoi disabilitare il reloader anche quando usi il flask runcomando:

FLASK_DEBUG=1 flask run --no-reload

Puoi cercare la WERKZEUG_RUN_MAINvariabile di ambiente se desideri rilevare quando sei nel processo figlio di ricaricamento:

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

Tuttavia, se hai bisogno di impostare le variabili globali del modulo, dovresti invece usare il @app.before_first_requestdecoratore su una funzione e fare in modo che quella funzione configuri tali globali. Verrà chiamato solo una volta dopo ogni ricarica quando arriva la prima richiesta:

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

Tieni presente che se lo esegui in un server WSGI su vasta scala che utilizza fork o nuovi sottoprocessi per gestire le richieste, i before_first_requestgestori potrebbero essere richiamati per ogni nuovo sottoprocesso.


2
Ah ok. Grazie per la spiegazione! Quindi è considerato un comportamento normale? Almeno bene che non c'è niente di sbagliato nel mio codice .. :)
kramer65

1
@ kramer65: è un comportamento del tutto normale e previsto. :-)
Martijn Pieters

1
Esiste un modo pratico per eseguire il codice di inizializzazione lento solo una volta, assicurandosi che venga chiamato anche durante l'esecuzione in WSGI (cioè non da app.run), ma senza attendere la prima richiesta? Non voglio che quella prima richiesta sia gravata dal costo di inizializzazione.
Kylotan

1
@ Kylotan: dovresti ispezionare l'ambiente; se imposti DEBUG solo durante l'esecuzione in fase di sviluppo, potresti cercare la WERKZEUG_RUN_MAINvariabile d'ambiente ed eseguire il codice solo quando DEBUGè falso o WERKZEUG_RUN_MAINè impostato, ad esempio. Diventa un po 'noioso.
Martijn Pieters

Giusto per chiarire, ho pensato che "ricaricare la funzionalità" significasse reattività (che vanificherebbe l'intero scopo dell'utilizzo dashper me). Per qualsiasi altro noobscome me, questo significa solo la funzionalità in cui la modifica / il salvataggio del file attiva un aggiornamento live.
Hendy

12

Se stai usando il flask runcomando moderno , non app.runviene utilizzata nessuna delle opzioni per . Per disabilitare completamente il reloader, passare --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Inoltre, __name__ == '__main__'non sarà mai vero perché l'app non viene eseguita direttamente. Usa le stesse idee dalla risposta di Martijn , tranne senza il __main__blocco.

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

Ho avuto lo stesso problema e l'ho risolto impostando app.debugsu False. Impostandolo su mi Trueveniva __name__ == "__main__"chiamato due volte.


Il mio __main__funziona ancora due volte con entrambi app.debug = Falsee app.run_server(debug=False). Sei sicuro che sia stato per te o potresti pubblicare del codice riproducibile da provare?
Hendy

Cambiare app.debug è stato tutto ciò che ho fatto per risolverlo. Potete confermare che main viene eseguito solo due volte all'avvio del server flask? Prova a eseguire un esempio minimo funzionante e verifica se il problema si verifica. Prova anche a eseguire un esempio minimo che non riesce in più versioni di Python, potrebbe essere stato un problema. Da allora ho migrato il mio progetto a Java e SparkJava invece che a python e flask, quindi non ricordo esattamente cosa ha risolto il problema.
Carvell Wakeman,

Sto usando flaskvia plotly dashe ho scoperto che hanno recentemente cambiato l' debug argomento predefinito passato a flask. Immagino di essermi sbagliato sopra e forse l'ho fatto app.debug=False(che forse è sovrascritto dagli argomenti predefiniti a run_server), o solo provato senza passare True, non impostando esplicitamente come mostrato sopra. Questo funziona correttamente per me ora (assicurandomi che debug=False). Grazie!
Hendy

2

Da Flask 0.11, si consiglia di eseguire l'app con flask runanziché con python application.py. L'utilizzo di quest'ultimo potrebbe comportare l'esecuzione del codice due volte.

Come affermato qui :

... dal Flask 0.11 in poi si consiglia il metodo Flask. La ragione di ciò è che a causa del modo in cui funziona il meccanismo di ricarica ci sono alcuni effetti collaterali bizzarri (come l'esecuzione di determinati codici due volte ...)


0

Uno dei possibili motivi per cui l'app Flask viene eseguita da sola due volte è una configurazione WEB_CONCURRENCYdell'impostazione su Heroku. Per creare uno, puoi scrivere in console heroku config:set WEB_CONCURRENCY=1


-1

Un'osservazione sui fili

Ciò è particolarmente fastidioso quando la tua applicazione utilizza i thread poiché verranno attivati ​​due volte all'avvio. Per quanto ho provato i single non rimediare neanche a questo (il che è sorprendente). Tuttavia, l'aggiunta di un ritardo iniziale di alcuni secondi prima dell'inizio del thread può risolvere il problema.

Se l'app si riavvia più rapidamente rispetto a prima che il periodo di ritardo sia terminato, il thread specificato viene generato solo una volta, dopo il riavvio.


@Dowvoter: ti interessa spiegare perché?
pfabri

-1

Ho avuto lo stesso problema. L'ho risolto modificando il mio main e inserendo use_reloader = False in esso. Se un corpo è qui alla ricerca di una soluzione alternativa per questo problema, il codice riportato di seguito ti consentirà di iniziare, tuttavia la funzionalità delle modifiche nel codice viene rilevata automaticamente e il riavvio dell'applicazione non funzionerà. Dovrai arrestare e riavviare manualmente l'applicazione dopo ogni modifica nel codice.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.