Отладчик Flask/Werkzeug, модель процесса и код инициализации

Я пишу веб-приложение Python, используя Flask. Мое приложение устанавливает соединение с другим сервером при запуске и периодически взаимодействует с этим сервером в фоновом режиме.

Если я не использую встроенный отладчик Flask (вызывая app.run с debug=False), проблем нет.

Если я использую встроенный отладчик (вызывая app.run с параметром debug=True), Flask запускает второй процесс Python с тем же кодом. Это дочерний процесс, который в конечном итоге прослушивает HTTP-соединения и в целом ведет себя так, как должно поступать мое приложение, и я предполагаю, что родитель просто следит за ним, когда срабатывает отладчик.

Однако это наносит ущерб моему коду запуска, который выполняется в обоих процессах; В итоге я получаю 2 подключения к внешнему серверу, 2 процесса, регистрирующиеся в одном и том же файле журнала, и, как правило, они спотыкаются друг о друга.

Я предполагаю, что мне не следует выполнять реальную работу перед вызовом app.run(), но куда я должен поместить этот код инициализации (который я хочу запускать только один раз для группы процессов Flask, независимо от режима отладчика, но который требует запускаться при запуске и независимо от клиентских запросов)?

Я нашел этот вопрос об "автоматической перезагрузке Flask и длительном потоке" что несколько связано, но несколько отличается, и ответ мне не помог. (У меня тоже есть отдельный длительный поток, помеченный как поток демона, но он уничтожается, когда запускается перезагрузка, но проблема, которую я пытаюсь решить, заключается в том, что до того, как должна произойти какая-либо перезагрузка. reload; меня беспокоит дополнительный процесс и правильный способ избежать выполнения ненужного кода в родительском процессе.)


person metamatt    schedule 20.07.2012    source источник
comment
Спасибо за этот вопрос (и ответ), это меня озадачило, когда я пытался сделать что-то подобное.   -  person akatkinson    schedule 14.10.2014


Ответы (1)


Я подтвердил, что такое поведение связано с Werkzeug, а не собственно с Flask, и связано с перезагрузкой. Вы можете увидеть это в файле serve.py Werkzeug — в run_simple(), если use_reloader имеет значение true, он вызывает make_server через вспомогательную функцию run_with_reloader()/restart_with_reloader(), которая выполняет subprocess.call(sys.executable) после установки переменная среды WERKZEUG_RUN_MAIN в среде, которая будет унаследована подпроцессом.

Я обошел это с помощью довольно уродливого хака: в моей основной функции перед созданием объекта приложения wsgi и вызовом app.run() я ищу WERKZEUG_RUN_MAIN:

if use_reloader and not os.environ.get('WERKZEUG_RUN_MAIN'):
    logger.warning('startup: pid %d is the werkzeug reloader' % os.getpid())
else:
    logger.warning('startup: pid %d is the active werkzeug' % os.getpid()
    # my real init code is invoked from here

У меня такое ощущение, что это было бы лучше сделать изнутри объекта приложения, если есть метод, который вызывается до того, как Werkzeug начнет его обслуживать. Но я не знаю такого метода.

Все это сводится к следующему: в run_simple.py Werkzeug будет только один возможный вызов make_server().serve_forever(), но может быть два вызова run_simple() (и весь стек вызовов до этого момента) прежде чем мы перейдем к make_server().

person metamatt    schedule 20.07.2012
comment
Я думаю, что этот ответ может быть устаревшим. Flask теперь имеет декоратор before_first_request. - person Mark E. Haase; 28.07.2015