I've been pulling my hair out trying to figure this one out, hoping someone else has already encountered this and knows how to solve it :)
I'm trying to build a very simple Flask endpoint that just needs to call a long running, blocking php
script (think while true {...}
). I've tried a few different methods to async launch the script, but the problem is my browser never actually receives the response back, even though the code for generating the response after running the script is executed.
I've tried using both multiprocessing
and threading
, neither seem to work:
# multiprocessing attempt
@app.route('/endpoint')
def endpoint():
def worker():
subprocess.Popen('nohup php script.php &', shell=True, preexec_fn=os.setpgrp)
p = multiprocessing.Process(target=worker)
print '111111'
p.start()
print '222222'
return json.dumps({
'success': True
})
# threading attempt
@app.route('/endpoint')
def endpoint():
def thread_func():
subprocess.Popen('nohup php script.php &', shell=True, preexec_fn=os.setpgrp)
t = threading.Thread(target=thread_func)
print '111111'
t.start()
print '222222'
return json.dumps({
'success': True
})
In both scenarios I see the 111111
and 222222
, yet my browser still hangs on the response from the endpoint. I've tried p.daemon = True
for the process, as well as p.terminate()
but no luck. I had hoped launching a script with nohup in a different shell and separate processs/thread would just work, but somehow Flask or uWSGI is impacted by it.
Since this does work locally on my Mac when I start my Flask app directly with python app.py
and hit it directly without going through my Nginx proxy and uWSGI, I'm starting to believe it may not be the code itself that is having issues. And because my Nginx just forwards the request to uWSGI, I believe it may possibly be something there that's causing it.
Here is my ini configuration for the domain for uWSGI, which I'm running in emperor mode:
[uwsgi]
protocol = uwsgi
max-requests = 5000
chmod-socket = 660
master = True
vacuum = True
enable-threads = True
auto-procname = True
procname-prefix = michael-
chdir = /srv/www/mysite.com
module = app
callable = app
socket = /tmp/mysite.com.sock
The program below is a simple Flask server. To run it you need to pip install flask shelljob. Save it to a file server.py and then run python server.py.
The following are 30 code examples of flask.request.endpoint () . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module flask.request , or try the search function .
If you don't have Flask installed, you can install it by running the following command in your terminal: We'll create a directory to use for this tutorial. For this tutorial, you need to login to the ParseHub client and create a project with ParseHub by following the dynamic filter tutorial.
Queue up the long-running task requested in a message broker. Respond to the user immediately so they can get back to their busy life. Handle the long-running task out of process. Notify the user when the task status is changed or is completed. Allow the user to check the status of the long-running task.
This kind of stuff is the actual and probably main use case for Python Celery
(https://docs.celeryproject.org/). As a general rule, do not run long-running jobs that are CPU-bound in the wsgi
process. It's tricky, it's inefficient, and most important thing, it's more complicated than setting up an async task in a celery worker. If you want to just prototype you can set the broker to memory
and not using an external server, or run a single-threaded redis
on the very same machine.
This way you can launch the task, call task.result()
which is blocking, but it blocks in an IO-bound fashion, or even better you can just return immediately by retrieving the task_id
and build a second endpoint /result?task_id=<task_id>
that checks if result is available:
result = AsyncResult(task_id, app=app)
if result.state == "SUCCESS":
return result.get()
else:
return result.state # or do something else depending on the state
This way you have a non-blocking wsgi
app that does what is best suited for: short time CPU-unbound calls that have IO calls at most with OS-level scheduling, then you can rely directly to the wsgi
server workers|processes|threads
or whatever you need to scale the API in whatever wsgi-server like uwsgi, gunicorn, etc. for the 99% of workloads as celery scales horizontally by increasing the number of worker processes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With