简体   繁体   中英

How can I run two flask servers in two threads/processes on two ports under one python program?

I'm writing a python debugging library which opens a flask server in a new thread and serves information about the program it's running in. This works fine when the program being debugged isn't a web server itself. However if I try to run it concurrently with another flask server that's running in debug mode, things break. When I try to access the second server, the result alternates between the two servers.

Here's an example:

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)

Now when I visit http://localhost:5002/ in my browser, the result may either be 1 or 2 instead of consistently being 2 .

Using multiprocessing.Process instead of Thread has the same result.

How does this happen, and how can I avoid it? Is it unavoidable with flask/werkzeug/WSGI? I like flask for its simplicity and ideally would like to continue using it. If that's not possible, what's the simplest library/framework that I can use that won't interfere with any other web servers running at the same time? I'd also like to use threads instead of processes if possible.

The reloader of werkzeug (which is used in debug mode by default) creates a new process using subprocess.call, simplified it does something like:

new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
subprocess.call([sys.executable] + sys.argv, env=new_environ, close_fds=False)

This means that your script is reexecuted, which is usually fine if all it contains is an app.run() , but in your case it would restart both app1 and app2, but both now use the same port because if the OS supports it the listening port is opened in the parent process, inherited by the child and used there directly if an environment variable WERKZEUG_SERVER_FD is set .

So now you have two different apps somehow using the same socket.

You can see this better if you add some output, eg:

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()

This prints for example:

PID: 18860
Werkzeug subprocess: None
Inherited FD: None
starting app1
starting app2
 * Running on http://127.0.0.1:5001/ (Press CTRL+C to quit)
 * Running on http://127.0.0.1:5002/ (Press CTRL+C to quit)
 * Restarting with inotify reloader
PID: 18864
Werkzeug subprocess: true
Inherited FD: 4
starting app1
starting app2
 * Debugger is active!

If you change the startup code to

if __name__ == '__main__':
    if os.environ.get("WERKZEUG_RUN_MAIN")) != 'true':
        Thread(target=start_app1).start()
    start_app2()

then it should work correctly, only app2 is reloaded by the reloader. However it runs in a separate process, not in a different thread, that is implied by using the debug mode.

A hack to avoid this would be to use:

if __name__ == '__main__':
    os.environ["WERKZEUG_RUN_MAIN"] = 'true'
    Thread(target=start_app1).start()
    start_app2()

Now the reloader thinks it's already running in the subprocess and doesn't start a new one, everything runs in the same process. Reloading won't work and I don't know what other side effects that may have.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM