Flask / Werkzeug调试器,进程模型和初始化代码

时间:2012-07-20 01:29:16

标签: python flask wsgi werkzeug

我正在使用Flask编写Python Web应用程序。我的应用程序在启动时建立与另一台服务器的连接,并在后台定期与该服务器通信。

如果我不使用Flask的内置调试器(使用debug = False调用app.run),没问题。

如果我使用内置调试器(使用debug = True调用app.run),Flask会使用相同的代码启动第二个Python进程。这是子进程最终监听HTTP连接,并且通常表现得像我的应用程序一样,并且我认为父进程只是在调试器启动时监视它。

然而,这会对我的启动代码造成严重破坏,该代码在两个进程中运行;我最终得到了2个与外部服务器的连接,2个进程记录到同一个日志文件,一般来说,它们相互绊倒。

我认为在调用app.run()之前我不应该做真正的工作,但是我应该在哪里放置这个初始化代码(我只想在Flask进程组中运行一次,无论调试器模式如何,但哪些需要在启动时运行并独立于客户端请求?)

我发现this question about "Flask auto-reload and long-running thread"有些相关,但有些不同,答案对我没有帮助。 (我也有一个单独的长时间运行的线程标记为一个守护程序线程,但它在重新加载器启动时被杀死,但我试图解决的问题是在任何重新加载需要发生之前。我不关心重新加载;我关注额外的进程,以及避免在父进程中执行不必要的代码的正确方法。)

1 个答案:

答案 0 :(得分:9)

我确认这种行为是由于Werkzeug,而不是Flask正确,并且它与重新加载器有关。您可以在Werkzeug的serve.py中看到这一点 - 在run_simple()中,如果use_reloader为true,它会通过辅助函数run_with_reloader()/ restart_with_reloader()调用make_server,该函数执行subprocess.call(sys.executable),然后设置环境变量WERKZEUG_RUN_MAIN将在子进程继承的环境中。

我用一个相当难看的hack来解决它:在我的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开始提供它之前调用了一个方法,那么从应用程序对象内部做得更好。不过,我不知道这种方法。

这一切归结为:在Werkzeug的run_simple.py中,只有最后一次调用make_server()。serve_forever(),但是可能有两次调用run_simple()(以及整个调用堆栈到在我们将它变为make_server()之前。