Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Responding to concurrent requests with Flask and eventlet

I try to set up a minimal Flask application that uses eventlet to respond to concurrent requests instantly instead of blocking and responding to one request after the other (as the standard Flask debugging webserver does).

Prerequisites:

pip install Flask
pip install eventlet

From my understanding by what I found so far on the internet, it should work like this:

# activate eventlet
import eventlet
eventlet.monkey_patch()

from flask import Flask

import datetime
from time import sleep

# create a new Flask application
app = Flask(__name__)

# a short running task that returns immediately
@app.route('/shortTask')
def short_running_task():
  start = datetime.datetime.now()
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# a long running tasks that returns after 30s
@app.route('/longTask')
def long_running_task():
  start = datetime.datetime.now()
  sleep(30)
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# run the webserver
if __name__ == '__main__':
    app.run(debug=True)

When running this file, then opening http://localhost:5000/longTask in a webbrowser tab and while it is still processing opening another tab with http://localhost:5000/shortTask, I would expect the second tab to return immediately while the first tab is still loading. However, similar to when running this on the standard Werkzeug server, the second tab only returns just after the first one is finished after 30s.

What is wrong here? By the way, would this be what is commonly referred to as a "production ready webserver" for Flask, given that there are only few concurrent users to be expected (5 at most)?

By the way, when I use the Flask-socketio library to run the webserver which, according to the documentation, automatically chooses eventlet if it is installed, then it works as expected.

Complete example with Flask-socketio:

# activate eventlet
import eventlet
eventlet.monkey_patch()

from flask import Flask
from flask_socketio import SocketIO

import datetime
from time import sleep

# create a new Flask application
app = Flask(__name__)

# activate Flask-socketio
socketio = SocketIO(app)

# a short running task that returns immediately
@app.route('/shortTask')
def short_running_task():
  start = datetime.datetime.now()
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# a long running tasks that returns after 30s
@app.route('/longTask')
def long_running_task():
  start = datetime.datetime.now()
  sleep(30)
  return 'Started at {0}, returned at {1}'.format(start, datetime.datetime.now())

# run the webserver with socketio
if __name__ == '__main__':
    socketio.run(app, debug=True)
like image 866
Dirk Avatar asked Jan 11 '16 13:01

Dirk


People also ask

Can Flask handle concurrent requests?

The server component that comes with Flask is really only meant for when you are developing your application; even though it can be configured to handle concurrent requests with app. run(threaded=True) (as of Flask 1.0 this is the default).

How does Flask handle multiple concurrent requests?

Flask will process one request per thread at the same time. If you have 2 processes with 4 threads each, that's 8 concurrent requests. Flask doesn't spawn or manage threads or processes.

Are flasks multithreaded?

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. As a demonstration purpose, I put sleep(1) before returning the response.

How does Gunicorn handle multiple requests?

If each request takes 10 milliseconds, a single worker dishes out 100 RPS. If some requests take 10 milliseconds, others take, say, up to 5 seconds, then you'll need more than one concurrent worker, so the one request that takes 5 seconds does not "hog" all of your serving capability.


1 Answers

When you run app.run(debug=True) you are explicitly telling Flask to run your application on the development web server, which is based on Werkzeug. It does not matter that you have loaded eventlet.

If you want to run your application on the eventlet web server, you have to start an eventlet web server, which according to the documentation is started as follows:

wsgi.server(eventlet.listen(('', 8000)), your_app)

This is more or less what socketio.run() does in my Flask-SocketIO extension, with a little bit more complexity to optionally handle SSL. The lines of code that do this are: https://github.com/miguelgrinberg/Flask-SocketIO/blob/539cd158f49ce085151911cb63edbacd0fa37173/flask_socketio/init.py#L391-L408. If you look around those lines, you will see that there are three different start up chunks of code, one for werkzeug, one for eventlet and one for gevent. They are all different.

like image 110
Miguel Avatar answered Sep 23 '22 03:09

Miguel