Чому запуск сервера розробників Flask працює два рази?


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 ###################

Хоча насправді це не проблема (решта працює, як очікувалося), мені просто цікаво, чому це так поводиться? Якісь ідеї?

Відповіді:


153

Перезавантажувач 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), але не чекаючи першого запиту? Я не хочу, щоб перший запит був обтяжений витратами на ініціалізацію.
Кілотан

1
@Kylotan: вам доведеться оглянути навколишнє середовище; якщо ви встановлюєте DEBUG лише під час запуску в розробці, ви можете шукати WERKZEUG_RUN_MAINзмінну середовища і запускати свій код, лише якщо DEBUGзначення false або WERKZEUG_RUN_MAINвстановлено, наприклад. Стає трохи втомливим.
Мартін Пітерс

Тільки для уточнення, я думав, що "перезавантаження функціональності" означає реактивність (що перемогло б ціль використання dashдля мене). Для будь-якого іншого, noobsяк я, це означає лише функціональність, при якій редагування / збереження файлу запускає оновлення в режимі реального часу.
Hendy

12

Якщо ви використовуєте сучасну flask runкоманду, жоден з параметрів app.run, що використовуються, не використовується. Щоб повністю вимкнути перезавантажувач, перейдіть --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Крім того, __name__ == '__main__'це ніколи не буде правдою, оскільки додаток не виконується безпосередньо. Використовуйте ті самі ідеї відповіді Мартіна , крім без __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 = Falseі раніше працює двічі з обома і app.run_server(debug=False). Ви впевнені, що це зробили за вас, чи ви можете опублікувати якийсь відтворюваний код, щоб спробувати?
Hendy

Зміна app.debug - це все, що я зробив, щоб вирішити це для мене. Чи можете ви підтвердити, що main запускається лише двічі при запуску колбового сервера? Спробуйте запустити мінімальний робочий приклад і перевірте, чи не виникає проблема. Також спробуйте запустити мінімальний приклад, який не працює в декількох версіях python, що, можливо, мало проблеми. З тих пір я переніс свій проект на Java та SparkJava замість python та flask, тому я не пам’ятаю, що саме вирішило проблему.
Carvell Wakeman

Я використовую flaskчерез plotly dash, і виявив, що нещодавно вони змінили debug аргумент за замовчуванням, переданий на flask. Я збираюся здогадатися, що я помилився вище і, можливо, зробив app.debug=False(що, можливо, замінено аргументами за замовчуванням на run_server), або лише спробував, не передаючи True, не встановлюючи явно, як показано вище. Зараз у мене це працює правильно (переконайтесь, що це debug=False). Дякую!
Хенді,

2

З Настій 0.11, рекомендується , щоб запустити додаток з flask runчим python application.py. Використання останнього може призвести до запуску коду двічі.

Як зазначено тут :

... від колби 0,11 і далі рекомендується метод колби. Причиною цього є те, що через те, як працює механізм перезавантаження, є деякі химерні побічні ефекти (наприклад, виконання певного коду двічі ...)


0

Однією з можливих причин, чому програма Flask запускається двічі, є конфігурація WEB_CONCURRENCYналаштування на Heroku. Щоб встановити один, ви можете написати в консолі heroku config:set WEB_CONCURRENCY=1


-1

Спостереження щодо ниток

Це особливо дратує, коли ваша програма використовує потоки, оскільки вони запускатимуться двічі під час запуску. Наскільки я пробував одиночні, це теж не виправляє (що дивно). Однак додавання початкової затримки на кілька секунд до запуску потоку може вирішити проблему.

Якщо програма перезапускається швидше, ніж до закінчення періоду затримки, даний потік створюється лише один раз після перезапуску.


@Dowvoter: потрібно пояснити, чому?
pfabri

-1

У мене було те саме питання. Я вирішив це, змінивши свій основний файл і вставивши в нього use_reloader = False. Якщо будь-яке тіло шукає обхідний шлях для вирішення цієї проблеми, тоді наведений нижче код допоможе вам розпочати роботу, однак функціональність змін у коді буде виявлена ​​автоматично, а перезапуск програми не буде працювати. Вам доведеться вручну зупиняти та перезапускати програму після кожного редагування коду.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.