Ale*_*all 3 python multithreading wsgi werkzeug flask
我正在编写一个 python 调试库,它在一个新线程中打开一个烧瓶服务器,并提供有关它正在运行的程序的信息。当被调试的程序不是 Web 服务器本身时,这很好用。但是,如果我尝试与另一个在调试模式下运行的 Flask 服务器同时运行它,事情就会中断。当我尝试访问第二台服务器时,结果在两台服务器之间交替。
下面是一个例子:
from flask.app import Flask
from threading import Thread
# app1 represents my debugging library
app1 = Flask('app1')
@app1.route('/')
def foo():
return '1'
Thread(target=lambda: app1.run(port=5001)).start()
# Cannot change code after here as I'm not the one writing it
app2 = Flask('app2')
@app2.route('/')
def bar():
return '2'
app2.run(debug=True, port=5002)
Run Code Online (Sandbox Code Playgroud)
现在,当我在浏览器中访问http://localhost:5002/时,结果可能是1或2不是始终为2.
使用multiprocessing.Process而不是Thread具有相同的结果。
这是如何发生的,我该如何避免?烧瓶/werkzeug/WSGI 是不可避免的吗?我喜欢 Flask 的简单性,理想情况下希望继续使用它。如果这是不可能的,那么我可以使用且不会干扰同时运行的任何其他 Web 服务器的最简单的库/框架是什么?如果可能,我还想使用线程而不是进程。
的重载器werkzeug(默认情况下在调试模式下使用)使用subprocess.call创建一个新进程,简化它执行以下操作:
new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
subprocess.call([sys.executable] + sys.argv, env=new_environ, close_fds=False)
Run Code Online (Sandbox Code Playgroud)
这意味着你的脚本被重新执行,如果它包含的所有内容通常是一个 ,这通常很好app.run(),但在你的情况下它会重新启动 app1 和 app2,但现在两者都使用相同的端口,因为如果操作系统支持它,监听端口在父进程,由子进程继承并在设置环境变量时WERKZEUG_SERVER_FD直接在那里使用。
所以现在你有两个不同的应用程序以某种方式使用相同的套接字。
如果添加一些输出,您可以更好地看到这一点,例如:
from flask.app import Flask
from threading import Thread
import os
app1 = Flask('app1')
@app1.route('/')
def foo():
return '1'
def start_app1():
print("starting app1")
app1.run(port=5001)
app2 = Flask('app2')
@app2.route('/')
def bar():
return '2'
def start_app2():
print("starting app2")
app2.run(port=5002, debug=True)
if __name__ == '__main__':
print("PID:", os.getpid())
print("Werkzeug subprocess:", os.environ.get("WERKZEUG_RUN_MAIN"))
print("Inherited FD:", os.environ.get("WERKZEUG_SERVER_FD"))
Thread(target=start_app1).start()
start_app2()
Run Code Online (Sandbox Code Playgroud)
这打印例如:
PID:18860 Werkzeug 子流程:无 继承的FD:无 启动应用程序1 启动应用程序2 * 在 http://127.0.0.1:5001/ 上运行(按 CTRL+C 退出) * 在 http://127.0.0.1:5002/ 上运行(按 CTRL+C 退出) *使用inotify重新加载器重新启动 PID:18864 Werkzeug 子流程:true 继承FD:4 启动应用程序1 启动应用程序2 * 调试器处于活动状态!
如果将启动代码更改为
if __name__ == '__main__':
if os.environ.get("WERKZEUG_RUN_MAIN")) != 'true':
Thread(target=start_app1).start()
start_app2()
Run Code Online (Sandbox Code Playgroud)
那么它应该可以正常工作,只有 app2 被重新加载器重新加载。然而,它运行在一个单独的进程中,而不是在不同的线程中,这是使用调试模式隐含的。
避免这种情况的黑客方法是使用:
if __name__ == '__main__':
os.environ["WERKZEUG_RUN_MAIN"] = 'true'
Thread(target=start_app1).start()
start_app2()
Run Code Online (Sandbox Code Playgroud)
现在重新加载器认为它已经在子进程中运行并且不会启动一个新进程,一切都在同一个进程中运行。重新加载将不起作用,我不知道可能有什么其他副作用。
| 归档时间: |
|
| 查看次数: |
3834 次 |
| 最近记录: |