Почему сервер Flask dev запускается дважды?

107

Я использую 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 ###################

Хотя на самом деле это не проблема (остальное работает, как ожидалось), мне просто интересно, почему он так себя ведет? Любые идеи?

kramer65
источник

Ответы:

154

Перезагрузчик 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
1
@ kramer65: это вполне нормальное и ожидаемое поведение. :-)
Мартин Питерс
1
Есть ли практический способ запустить медленный код инициализации только один раз, гарантируя, что он также будет вызываться при запуске под wsgi (т.е. не из app.run), но не дожидаясь первого запроса? Я не хочу, чтобы этот первый запрос был обременен стоимостью инициализации.
Kylotan
1
@Kylotan: вам нужно осмотреть окружающую среду; если вы устанавливаете DEBUG только при запуске в процессе разработки, вы можете искать WERKZEUG_RUN_MAINпеременную среды и запускать свой код, только если , например, DEBUGfalse или WERKZEUG_RUN_MAINустановлено. Становится немного утомительно.
Мартин Питерс
Чтобы прояснить, я думал, что «перезагрузка функциональности» означает реактивность (которая dashдля меня уничтожит всю цель использования ). Для любого другого noobs, такого как я, это означает только функциональность, при которой редактирование / сохранение файла запускает обновление в реальном времени.
Hendy
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
давидизм
источник
8

У меня была такая же проблема, и я решил ее, установив app.debugна False. Установка этого значения вызвала двойной вызов Truemy __name__ == "__main__".

Карвелл Уэйкман
источник
Мой по- __main__прежнему работает дважды с обоими app.debug = Falseи app.run_server(debug=False). Вы уверены, что это сделало это за вас, или вы могли бы опубликовать воспроизводимый код, чтобы попробовать?
Hendy
Изменение app.debug - это все, что я сделал, чтобы решить эту проблему. Можете ли вы подтвердить, что main запускается только дважды при запуске сервера flask? Попробуйте запустить минимальный рабочий пример и посмотрите, возникает ли проблема. Также попробуйте запустить минимальный пример, который не работает в нескольких версиях python, что могло быть проблемой. С тех пор я перенес свой проект на Java и SparkJava вместо python и flask, поэтому я точно не помню, что решило проблему.
Carvell Wakeman
Я использую flaskvia plotly dashи обнаружил, что недавно они изменили debug переданный аргумент по умолчаниюflask . Я собираюсь предположить, что я ошибся выше и, возможно, ошибся app.debug=False(что, возможно, переопределено аргументами по умолчанию run_server), или попытался только без передачи True, а не с явной настройкой, как показано выше. Теперь у меня это работает правильно (убедившись в этом debug=False). Спасибо!
Hendy
2

Начиная с Flask 0.11, рекомендуется запускать приложение с использованием, flask runа не python application.py. Использование последнего может привести к двойному запуску вашего кода.

Как указано здесь :

... начиная с Flask 0.11 и далее рекомендуется использовать метод колбы. Причина в том, что из-за того, как работает механизм перезагрузки, возникают некоторые странные побочные эффекты (например, выполнение определенного кода дважды ...)

salsa_man
источник
0

Одна из возможных причин, по которой приложение Flask запускается дважды, - это конфигурация WEB_CONCURRENCYнастроек на Heroku. Чтобы установить в один, вы можете написать в консоли heroku config:set WEB_CONCURRENCY=1

Trojek
источник
-1

Я была такая же проблема. Я решил это, изменив свой основной и вставив в него use_reloader = False. Если кто-то здесь ищет обходной путь для этой проблемы, то приведенный ниже код поможет вам начать работу, однако вы обнаружите, что изменения в коде автоматически обнаруживаются и перезапуск приложения не будет работать. Вам придется вручную останавливать и перезапускать приложение после каждого редактирования кода.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
ЛА
источник