8

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

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

如果我确实使用内置调试器(使用 debug=True 调用 app.run),Flask 会使用相同的代码启动第二个 Python 进程。最终侦听 HTTP 连接并通常按照我的应用程序应有的行为是子进程,我认为当调试器启动时父进程只是在那里监视它。

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

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

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

4

1 回答 1

9

我确认这种行为是由于 Werkzeug 而不是 Flask 本身,它与重新加载器有关。你可以在 Werkzeug 的 serving.py 中看到这一点——在 run_simple() 中,如果 use_reloader 为真,它会通过一个辅助函数 run_with_reloader() / restart_with_reloader() 调用 make_server,它会在设置一个 subprocess.call(sys.executable) 之后环境变量 WERKZEUG_RUN_MAIN 将被子进程继承。

我用一个相当丑陋的 hack 来解决它:在我的主函数中,在创建 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() 之前。

于 2012-07-20T06:09:49.850 回答