Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threading in Flask not working with UWSGI but working on commandline

I have a Flask application that works fine when run on the commandline, but when it is run through uWSGI it doesn't respond correctly to requests or the worker thread doesn't work properly. I've rewritten a simple proof-of-concept/failure program that demonstrates the issue:

from datetime import datetime
from threading import Event, Thread

from flask import Flask


class JobManager:
    def __init__(self):
        self.running = False
        self.event = Event()

    def start(self):
        self.running = True
        while self.running:
            print("Processing Job at", datetime.now().strftime('%c'))
            self.event.clear()
            self.event.wait(5)
            if self.event.is_set():
                print("Interrupted by request!")

    def stop(self):
        self.running = False
        self.event.set()


app = Flask(__name__)
jobs = JobManager()

t = Thread(target=jobs.start)
t.start()

@app.route('/')
def hello_world():
    global jobs
    jobs.event.set()

    return "I'm alive at " + datetime.now().strftime('%c')


if __name__ == '__main__':
    app.run()

I'd expect that calling the / route would print "Interrupted by request!" on the console but it just hangs even though the job should be running in a seperate thread.

My uWSGI configuration is:

[uwsgi]
module = app:app

master = true
processes = 5
threads = 2

socket = 0.0.0.0:5000
protocol = http

reload-mercy = 5
worker-reload-mercy = 5

die-on-term = true
enable-threads = true
thunder-lock = true

logto = /home/user/dev/flask-thread/uwsgi_log.log
logto2 = /home/user/dev/flask-thread/uwsgi2_log.log

env = PATH=/home/user/dev/flask-thread/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

I then run uWSGI in the venv with: uwsgi --ini uwsgi-test.ini

It will work if I use python app.py and use the built in flask development server.

My only guess is that it has something to do with the GIL interacting with uWSGI but that's a wild guess and I have no idea how to stop that.

like image 210
AndrewWhalan Avatar asked Aug 20 '18 12:08

AndrewWhalan


People also ask

How do I enable threads on uWSGI?

You must enable uWSGI threads by adding the --enable-threads option in the uwsgi command. This option is applied automatically when you specify the --threads option to configure the number of threads.

Is flask threaded by default?

As of Flask 1.0, flask server is multi-threaded by default. Each new request is handled in a new thread. This is a simple Flask application using default settings.

What is uWSGI flask?

uWSGI is a fast, compiled server suite with extensive configuration and capabilities beyond a basic server. It can be very performant due to being a compiled program. It is complex to configure beyond the basic application, and has so many options that it can be difficult for beginners to understand.

What does threaded true do in flask?

With threaded=True requests are each handled in a new thread. How many threads your server can handle concurrently depends entirely on your OS and what limits it sets on the number of threads per process. The implementation uses the SocketServer.


2 Answers

For me using the uwsgi flag --lazy worked.

uwsgi foo.ini --socket=0.0.0.0:5000 --protocol=http --lazy -w wsgi

Explanation:

Quote from http://lists.unbit.it/pipermail/uwsgi/2011-June/002307.html:

single process (no master):

the process creates the thread in which the WSGI callable put data.

This is ok

with master process:

the master load the app and generate the thread. Then it forks one or more workers. This workers are new processes with no relation with the thread spawned in the master. So your data will go nowhere (only the master can access the initial thread).

You have two way to follow:

1) add --lazy to the command line, in this way your app will be loaded after master's fork, so each worker will get its thread.

2) rewrite your app to use the uwsgi.post_fork_hook feature.

like image 152
fivef Avatar answered Sep 26 '22 02:09

fivef


For me, lazy didn't work, and I didn't want to go through the hassle of using the post_fork_hook feature, but I did find the following on https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html :

By default the Python plugin does not initialize the GIL. This means your app-generated threads will not run. If you need threads, remember to enable them with enable-threads. Running uWSGI in multithreading mode (with the threads options) will automatically enable threading support. This “strange” default behavior is for performance reasons, no shame in that.

No shame, indeed, but a heartache nonetheless. Long story short, I set enable-thread = true in my config file, and my threads began spinning off immediately and reliably

like image 32
MANA624 Avatar answered Sep 26 '22 02:09

MANA624