https://github.com/iximiuz/flask-gevent-tutorial
How to use Flask with gevent (uWSGI and Gunicorn editions)
https://github.com/iximiuz/flask-gevent-tutorial
asynchronous asyncio flask gevent gunicorn nginx python uwsgi
Last synced: 3 months ago
JSON representation
How to use Flask with gevent (uWSGI and Gunicorn editions)
- Host: GitHub
- URL: https://github.com/iximiuz/flask-gevent-tutorial
- Owner: iximiuz
- License: mit
- Created: 2019-12-27T22:31:45.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2019-12-29T10:57:50.000Z (over 5 years ago)
- Last Synced: 2025-03-14T21:46:38.021Z (3 months ago)
- Topics: asynchronous, asyncio, flask, gevent, gunicorn, nginx, python, uwsgi
- Language: Python
- Homepage: https://iximiuz.com/en/posts/flask-gevent-tutorial/?utm_medium=github&utm_source=repo
- Size: 15.6 KB
- Stars: 94
- Watchers: 3
- Forks: 28
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# How to use Flask with gevent (uWSGI and Gunicorn editions)
## Create simple Flask application
First, we need to emulate a slow 3rd party API:
```python
# slow_api/api.py
import osimport asyncio
from aiohttp import webasync def handle(request):
delay = float(request.query.get('delay') or 1)
await asyncio.sleep(delay)
return web.Response(text='slow api response')app = web.Application()
app.add_routes([web.get('/', handle)])if __name__ == '__main__':
web.run_app(app, port=os.environ['PORT'])
```Then, we create a simple flask application with a dependency on the slow 3rd party API:
```python
# flask_app/app.py
import osimport requests
from flask import Flask, requestapi_port = os.environ['PORT_API']
api_url = f'http://slow_api:{api_port}/'app = Flask(__name__)
@app.route('/')
def index():
delay = float(request.args.get('delay') or 1)
resp = requests.get(f'{api_url}?delay={delay}')
return 'Hi there! ' + resp.text
```## Deploy Flask application using Flask dev server
```bash
# Build and start app served by Flask dev server
$ docker-compose -f sync-devserver.yml build
$ docker-compose -f sync-devserver.yml up# Test single-threaded deployment
$ ab -r -n 10 -c 5 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 5
> Time taken for tests: 10.139 seconds
> Complete requests: 10
> Failed requests: 0
> Requests per second: 0.99 [#/sec] (mean)# Test multi-threaded deployment
$ ab -r -n 10 -c 5 http://127.0.0.1:3001/?delay=1
> Concurrency Level: 5
> Time taken for tests: 3.069 seconds
> Complete requests: 10
> Failed requests: 0
> Requests per second: 3.26 [#/sec] (mean)
```## Deploy Flask application using uWSGI (4 worker processes x 50 threads each)
```bash
# Build and start app served by uWSGI
$ docker-compose -f sync-uwsgi.yml build
$ docker-compose -f sync-uwsgi.yml up$ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 200
> Time taken for tests: 12.685 seconds
> Complete requests: 2000
> Failed requests: 0
> Requests per second: 157.67 [#/sec] (mean)
```## Deploy Flask application using Gunicorn (4 worker processes x 50 threads each)
```bash
# Build and start app served by Gunicorn
$ docker-compose -f sync-gunicorn.yml build
$ docker-compose -f sync-gunicorn.yml up$ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 200
> Time taken for tests: 13.427 seconds
> Complete requests: 2000
> Failed requests: 0
> Requests per second: 148.95 [#/sec] (mean)
```## Deploy Flask application using gevent.pywsgi
First, we need to create an entrypoint:
```python
# flask_app/pywsgi.py
from gevent import monkey
monkey.patch_all()import os
from gevent.pywsgi import WSGIServer
from app import apphttp_server = WSGIServer(('0.0.0.0', int(os.environ['PORT_APP'])), app)
http_server.serve_forever()
```Notice, how it patches our flask application. Without `monkey.patch_all()` there would be no benefit from using gevent here.
```bash
# Build and start app served by gevent.pywsgi
$ docker-compose -f async-gevent-pywsgi.yml build
$ docker-compose -f async-gevent-pywsgi.yml up$ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 200
> Time taken for tests: 17.536 seconds
> Complete requests: 2000
> Failed requests: 0
> Requests per second: 114.05 [#/sec] (mean)
```## Deploy Flask application using uWSGI + gevent
First, we need to create an entrypoint:
```python
# flask_app/patched.py
from gevent import monkey
monkey.patch_all()from app import app # re-export
```We need to patch very early.
```bash
# Build and start app served by uWSGI + gevent
$ docker-compose -f async-gevent-uwsgi.yml build
$ docker-compose -f async-gevent-uwsgi.yml up$ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1
> Time taken for tests: 13.164 seconds
> Complete requests: 2000
> Failed requests: 0
> Requests per second: 151.93 [#/sec] (mean)
```## Deploy Flask application using Gunicorn + gevent
This setup uses the same `patched.py` entrypoint.
```bash
# Build and start app served by Gunicorn + gevent
$ docker-compose -f async-gevent-gunicorn.yml build
$ docker-compose -f async-gevent-gunicorn.yml up$ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 200
> Time taken for tests: 17.839 seconds
> Complete requests: 2000
> Failed requests: 0
> Requests per second: 112.11 [#/sec] (mean)
```## Use Nginx reverse proxy in front of application server
See `nginx-gunicorn.yml` and `nginx-uwsgi.yml`:
```bash
$ docker-compose -f nginx-gunicorn.yml build
$ docker-compose -f nginx-gunicorn.yml up# or
$ docker-compose -f nginx-uwsgi.yml build
$ docker-compose -f nginx-uwsgi.yml up# and then:
$ ab -r -n 2000 -c 200 http://127.0.0.1:8080/?delay=1
> ...
```## Bonus: make psycopg2 gevent-friendly with psycogreen
gevent patches only modules from the Python standard library. If we use
3rd party modules, like psycopg2, corresponding IO will still be blocking:```python
# psycopg2/app.pyfrom gevent import monkey
monkey.patch_all()import os
import psycopg2
import requests
from flask import Flask, requestapi_port = os.environ['PORT_API']
api_url = f'http://slow_api:{api_port}/'app = Flask(__name__)
@app.route('/')
def index():
conn = psycopg2.connect(user="example", password="example", host="postgres")
delay = float(request.args.get('delay') or 1)
resp = requests.get(f'{api_url}?delay={delay/2}')cur = conn.cursor()
cur.execute("SELECT NOW(), pg_sleep(%s)", (delay/2,))
return 'Hi there! {} {}'.format(resp.text, cur.fetchall()[0])
```We expect ~2 seconds to perform 10 one-second-long HTTP requests with concurrency 5,
but the test shows >5 seconds due to the blocking behavior of psycopg2 calls:```bash
$ docker-compose -f bonus-psycopg2-gevent.yml build
$ docker-compose -f bonus-psycopg2-gevent.yml up$ ab -r -n 10 -c 5 http://127.0.0.1:3000/?delay=1
> Concurrency Level: 5
> Time taken for tests: 6.670 seconds
> Complete requests: 10
> Failed requests: 0
> Requests per second: 1.50 [#/sec] (mean)
```To bypass this limitation, we need to use psycogreen module to patch psycopg2:
```python
# psycopg2/patched.pyfrom psycogreen.gevent import patch_psycopg
patch_psycopg()from app import app
``````bash
$ docker-compose -f bonus-psycopg2-gevent.yml build
$ docker-compose -f bonus-psycopg2-gevent.yml up$ ab -r -n 10 -c 5 http://127.0.0.1:3001/?delay=1
> Concurrency Level: 5
> Time taken for tests: 3.148 seconds
> Complete requests: 10
> Failed requests: 0
> Requests per second: 3.18 [#/sec] (mean)
```