├── .gitignore ├── LICENSE.txt ├── README.md ├── async-gevent-gunicorn.yml ├── async-gevent-pywsgi.yml ├── async-gevent-uwsgi.yml ├── bonus-psycopg2-gevent.yml ├── flask_app ├── Dockerfile-devserver ├── Dockerfile-gevent-gunicorn ├── Dockerfile-gevent-pywsgi ├── Dockerfile-gevent-uwsgi ├── Dockerfile-gunicorn ├── Dockerfile-uwsgi ├── app.py ├── nginx-gunicorn.conf ├── nginx-uwsgi.conf ├── patched.py └── pywsgi.py ├── nginx-gunicorn.yml ├── nginx-uwsgi.yml ├── psycopg2 ├── Dockerfile ├── app.py └── patched.py ├── slow_api ├── Dockerfile └── api.py ├── sync-devserver.yml ├── sync-gunicorn.yml └── sync-uwsgi.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Velichko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to use Flask with gevent (uWSGI and Gunicorn editions) 2 | 3 | ## Create simple Flask application 4 | 5 | First, we need to emulate a slow 3rd party API: 6 | 7 | ```python 8 | # slow_api/api.py 9 | import os 10 | 11 | import asyncio 12 | from aiohttp import web 13 | 14 | async def handle(request): 15 | delay = float(request.query.get('delay') or 1) 16 | await asyncio.sleep(delay) 17 | return web.Response(text='slow api response') 18 | 19 | app = web.Application() 20 | app.add_routes([web.get('/', handle)]) 21 | 22 | if __name__ == '__main__': 23 | web.run_app(app, port=os.environ['PORT']) 24 | ``` 25 | 26 | Then, we create a simple flask application with a dependency on the slow 3rd party API: 27 | 28 | ```python 29 | # flask_app/app.py 30 | import os 31 | 32 | import requests 33 | from flask import Flask, request 34 | 35 | api_port = os.environ['PORT_API'] 36 | api_url = f'http://slow_api:{api_port}/' 37 | 38 | app = Flask(__name__) 39 | 40 | @app.route('/') 41 | def index(): 42 | delay = float(request.args.get('delay') or 1) 43 | resp = requests.get(f'{api_url}?delay={delay}') 44 | return 'Hi there! ' + resp.text 45 | ``` 46 | 47 | ## Deploy Flask application using Flask dev server 48 | 49 | ```bash 50 | # Build and start app served by Flask dev server 51 | $ docker-compose -f sync-devserver.yml build 52 | $ docker-compose -f sync-devserver.yml up 53 | 54 | # Test single-threaded deployment 55 | $ ab -r -n 10 -c 5 http://127.0.0.1:3000/?delay=1 56 | > Concurrency Level: 5 57 | > Time taken for tests: 10.139 seconds 58 | > Complete requests: 10 59 | > Failed requests: 0 60 | > Requests per second: 0.99 [#/sec] (mean) 61 | 62 | # Test multi-threaded deployment 63 | $ ab -r -n 10 -c 5 http://127.0.0.1:3001/?delay=1 64 | > Concurrency Level: 5 65 | > Time taken for tests: 3.069 seconds 66 | > Complete requests: 10 67 | > Failed requests: 0 68 | > Requests per second: 3.26 [#/sec] (mean) 69 | ``` 70 | 71 | ## Deploy Flask application using uWSGI (4 worker processes x 50 threads each) 72 | 73 | ```bash 74 | # Build and start app served by uWSGI 75 | $ docker-compose -f sync-uwsgi.yml build 76 | $ docker-compose -f sync-uwsgi.yml up 77 | 78 | $ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1 79 | > Concurrency Level: 200 80 | > Time taken for tests: 12.685 seconds 81 | > Complete requests: 2000 82 | > Failed requests: 0 83 | > Requests per second: 157.67 [#/sec] (mean) 84 | ``` 85 | 86 | ## Deploy Flask application using Gunicorn (4 worker processes x 50 threads each) 87 | 88 | ```bash 89 | # Build and start app served by Gunicorn 90 | $ docker-compose -f sync-gunicorn.yml build 91 | $ docker-compose -f sync-gunicorn.yml up 92 | 93 | $ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1 94 | > Concurrency Level: 200 95 | > Time taken for tests: 13.427 seconds 96 | > Complete requests: 2000 97 | > Failed requests: 0 98 | > Requests per second: 148.95 [#/sec] (mean) 99 | ``` 100 | 101 | ## Deploy Flask application using gevent.pywsgi 102 | 103 | First, we need to create an entrypoint: 104 | 105 | ```python 106 | # flask_app/pywsgi.py 107 | from gevent import monkey 108 | monkey.patch_all() 109 | 110 | import os 111 | from gevent.pywsgi import WSGIServer 112 | from app import app 113 | 114 | http_server = WSGIServer(('0.0.0.0', int(os.environ['PORT_APP'])), app) 115 | http_server.serve_forever() 116 | ``` 117 | 118 | Notice, how it patches our flask application. Without `monkey.patch_all()` there would be no benefit from using gevent here. 119 | 120 | ```bash 121 | # Build and start app served by gevent.pywsgi 122 | $ docker-compose -f async-gevent-pywsgi.yml build 123 | $ docker-compose -f async-gevent-pywsgi.yml up 124 | 125 | $ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1 126 | > Concurrency Level: 200 127 | > Time taken for tests: 17.536 seconds 128 | > Complete requests: 2000 129 | > Failed requests: 0 130 | > Requests per second: 114.05 [#/sec] (mean) 131 | ``` 132 | 133 | ## Deploy Flask application using uWSGI + gevent 134 | 135 | First, we need to create an entrypoint: 136 | 137 | ```python 138 | # flask_app/patched.py 139 | from gevent import monkey 140 | monkey.patch_all() 141 | 142 | from app import app # re-export 143 | ``` 144 | 145 | We need to patch very early. 146 | 147 | ```bash 148 | # Build and start app served by uWSGI + gevent 149 | $ docker-compose -f async-gevent-uwsgi.yml build 150 | $ docker-compose -f async-gevent-uwsgi.yml up 151 | 152 | $ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1 153 | > Time taken for tests: 13.164 seconds 154 | > Complete requests: 2000 155 | > Failed requests: 0 156 | > Requests per second: 151.93 [#/sec] (mean) 157 | ``` 158 | 159 | ## Deploy Flask application using Gunicorn + gevent 160 | 161 | This setup uses the same `patched.py` entrypoint. 162 | 163 | ```bash 164 | # Build and start app served by Gunicorn + gevent 165 | $ docker-compose -f async-gevent-gunicorn.yml build 166 | $ docker-compose -f async-gevent-gunicorn.yml up 167 | 168 | $ ab -r -n 2000 -c 200 http://127.0.0.1:3000/?delay=1 169 | > Concurrency Level: 200 170 | > Time taken for tests: 17.839 seconds 171 | > Complete requests: 2000 172 | > Failed requests: 0 173 | > Requests per second: 112.11 [#/sec] (mean) 174 | ``` 175 | 176 | ## Use Nginx reverse proxy in front of application server 177 | 178 | See `nginx-gunicorn.yml` and `nginx-uwsgi.yml`: 179 | 180 | ```bash 181 | $ docker-compose -f nginx-gunicorn.yml build 182 | $ docker-compose -f nginx-gunicorn.yml up 183 | 184 | # or 185 | 186 | $ docker-compose -f nginx-uwsgi.yml build 187 | $ docker-compose -f nginx-uwsgi.yml up 188 | 189 | # and then: 190 | 191 | $ ab -r -n 2000 -c 200 http://127.0.0.1:8080/?delay=1 192 | > ... 193 | ``` 194 | 195 | ## Bonus: make psycopg2 gevent-friendly with psycogreen 196 | 197 | gevent patches only modules from the Python standard library. If we use 198 | 3rd party modules, like psycopg2, corresponding IO will still be blocking: 199 | 200 | ```python 201 | # psycopg2/app.py 202 | 203 | from gevent import monkey 204 | monkey.patch_all() 205 | 206 | import os 207 | 208 | import psycopg2 209 | import requests 210 | from flask import Flask, request 211 | 212 | api_port = os.environ['PORT_API'] 213 | api_url = f'http://slow_api:{api_port}/' 214 | 215 | app = Flask(__name__) 216 | 217 | @app.route('/') 218 | def index(): 219 | conn = psycopg2.connect(user="example", password="example", host="postgres") 220 | delay = float(request.args.get('delay') or 1) 221 | resp = requests.get(f'{api_url}?delay={delay/2}') 222 | 223 | cur = conn.cursor() 224 | cur.execute("SELECT NOW(), pg_sleep(%s)", (delay/2,)) 225 | 226 | return 'Hi there! {} {}'.format(resp.text, cur.fetchall()[0]) 227 | ``` 228 | 229 | We expect ~2 seconds to perform 10 one-second-long HTTP requests with concurrency 5, 230 | but the test shows >5 seconds due to the blocking behavior of psycopg2 calls: 231 | 232 | ```bash 233 | $ docker-compose -f bonus-psycopg2-gevent.yml build 234 | $ docker-compose -f bonus-psycopg2-gevent.yml up 235 | 236 | $ ab -r -n 10 -c 5 http://127.0.0.1:3000/?delay=1 237 | > Concurrency Level: 5 238 | > Time taken for tests: 6.670 seconds 239 | > Complete requests: 10 240 | > Failed requests: 0 241 | > Requests per second: 1.50 [#/sec] (mean) 242 | ``` 243 | 244 | To bypass this limitation, we need to use psycogreen module to patch psycopg2: 245 | 246 | 247 | ```python 248 | # psycopg2/patched.py 249 | 250 | from psycogreen.gevent import patch_psycopg 251 | patch_psycopg() 252 | 253 | from app import app 254 | ``` 255 | 256 | ```bash 257 | $ docker-compose -f bonus-psycopg2-gevent.yml build 258 | $ docker-compose -f bonus-psycopg2-gevent.yml up 259 | 260 | $ ab -r -n 10 -c 5 http://127.0.0.1:3001/?delay=1 261 | > Concurrency Level: 5 262 | > Time taken for tests: 3.148 seconds 263 | > Complete requests: 10 264 | > Failed requests: 0 265 | > Requests per second: 3.18 [#/sec] (mean) 266 | ``` 267 | 268 | -------------------------------------------------------------------------------- /async-gevent-gunicorn.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-gevent-gunicorn 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - WORKERS=1 12 | ports: 13 | - "127.0.0.1:3000:3000" 14 | depends_on: 15 | - slow_api 16 | 17 | slow_api: 18 | init: true 19 | build: ./slow_api 20 | environment: 21 | - PORT=4000 22 | expose: 23 | - "4000" 24 | 25 | -------------------------------------------------------------------------------- /async-gevent-pywsgi.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-gevent-pywsgi 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - THREADS=without 12 | ports: 13 | - "127.0.0.1:3000:3000" 14 | depends_on: 15 | - slow_api 16 | 17 | slow_api: 18 | init: true 19 | build: ./slow_api 20 | environment: 21 | - PORT=4000 22 | expose: 23 | - "4000" 24 | 25 | -------------------------------------------------------------------------------- /async-gevent-uwsgi.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-gevent-uwsgi 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - WORKERS=2 12 | - ASYNC_CORES=2000 13 | - PROTOCOL=http 14 | ports: 15 | - "127.0.0.1:3000:3000" 16 | depends_on: 17 | - slow_api 18 | 19 | slow_api: 20 | init: true 21 | build: ./slow_api 22 | environment: 23 | - PORT=4000 24 | expose: 25 | - "4000" 26 | 27 | -------------------------------------------------------------------------------- /bonus-psycopg2-gevent.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app: 4 | init: true 5 | build: 6 | context: ./psycopg2 7 | dockerfile: Dockerfile 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - WORKERS=1 12 | - ASYNC_CORES=2000 13 | - MODULE=app 14 | ports: 15 | - "127.0.0.1:3000:3000" 16 | depends_on: 17 | - slow_api 18 | - postgres 19 | 20 | flask_app_2: 21 | init: true 22 | build: 23 | context: ./psycopg2 24 | dockerfile: Dockerfile 25 | environment: 26 | - PORT_APP=3001 27 | - PORT_API=4000 28 | - WORKERS=1 29 | - ASYNC_CORES=2000 30 | - MODULE=patched 31 | ports: 32 | - "127.0.0.1:3001:3001" 33 | depends_on: 34 | - slow_api 35 | - postgres 36 | 37 | slow_api: 38 | init: true 39 | build: ./slow_api 40 | environment: 41 | - PORT=4000 42 | expose: 43 | - "4000" 44 | 45 | postgres: 46 | image: postgres 47 | environment: 48 | POSTGRES_USER: example 49 | POSTGRES_PASSWORD: example 50 | expose: 51 | - "5432" 52 | 53 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-devserver: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests 4 | 5 | COPY app.py /app.py 6 | 7 | ENV FLASK_APP=app 8 | 9 | CMD flask run --no-reload \ 10 | --$THREADS-threads \ 11 | --host 0.0.0.0 --port $PORT_APP 12 | 13 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-gevent-gunicorn: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests gunicorn gevent 4 | 5 | COPY app.py /app.py 6 | COPY patched.py /patched.py 7 | 8 | CMD gunicorn --worker-class gevent \ 9 | --workers $WORKERS \ 10 | --bind 0.0.0.0:$PORT_APP \ 11 | patched:app 12 | 13 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-gevent-pywsgi: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests gevent 4 | 5 | COPY app.py /app.py 6 | COPY pywsgi.py /pywsgi.py 7 | 8 | CMD python /pywsgi.py 9 | 10 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-gevent-uwsgi: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests uwsgi gevent 4 | 5 | COPY app.py /app.py 6 | COPY patched.py /patched.py 7 | 8 | CMD uwsgi --master \ 9 | --single-interpreter \ 10 | --workers $WORKERS \ 11 | --gevent $ASYNC_CORES \ 12 | --protocol $PROTOCOL \ 13 | --socket 0.0.0.0:$PORT_APP \ 14 | --module patched:app 15 | 16 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-gunicorn: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests gunicorn 4 | 5 | COPY app.py /app.py 6 | 7 | CMD gunicorn --workers $WORKERS \ 8 | --threads $THREADS \ 9 | --bind 0.0.0.0:$PORT_APP \ 10 | app:app 11 | 12 | -------------------------------------------------------------------------------- /flask_app/Dockerfile-uwsgi: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests uwsgi 4 | 5 | COPY app.py /app.py 6 | 7 | CMD uwsgi --master \ 8 | --workers $WORKERS \ 9 | --threads $THREADS \ 10 | --protocol $PROTOCOL \ 11 | --socket 0.0.0.0:$PORT_APP \ 12 | --module app:app 13 | 14 | -------------------------------------------------------------------------------- /flask_app/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | from flask import Flask, request 5 | 6 | api_port = os.environ['PORT_API'] 7 | api_url = f'http://slow_api:{api_port}/' 8 | 9 | app = Flask(__name__) 10 | 11 | @app.route('/') 12 | def index(): 13 | delay = int(request.args.get('delay') or 1) 14 | resp = requests.get(f'{api_url}?delay={delay}') 15 | return 'Hi there! ' + resp.text 16 | 17 | -------------------------------------------------------------------------------- /flask_app/nginx-gunicorn.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | location / { 5 | proxy_set_header Host $http_host; 6 | proxy_set_header X-Real-IP $remote_addr; 7 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 8 | proxy_set_header X-Forwarded-Proto $scheme; 9 | 10 | proxy_pass http://flask_app:3000; 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /flask_app/nginx-uwsgi.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | location / { 5 | include uwsgi_params; 6 | uwsgi_pass uwsgi://flask_app:3000; 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /flask_app/patched.py: -------------------------------------------------------------------------------- 1 | from gevent import monkey 2 | monkey.patch_all() 3 | 4 | from app import app 5 | 6 | -------------------------------------------------------------------------------- /flask_app/pywsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from gevent import monkey 4 | from gevent.pywsgi import WSGIServer 5 | 6 | from app import app 7 | 8 | 9 | monkey.patch_all() 10 | 11 | http_server = WSGIServer(('0.0.0.0', int(os.environ['PORT_APP'])), app) 12 | http_server.serve_forever() 13 | 14 | -------------------------------------------------------------------------------- /nginx-gunicorn.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | ingress: 4 | image: nginx:1.17.6 5 | ports: 6 | - "127.0.0.1:8080:80" 7 | volumes: 8 | - ./flask_app/nginx-gunicorn.conf:/etc/nginx/conf.d/default.conf 9 | depends_on: 10 | - flask_app 11 | 12 | flask_app: 13 | init: true 14 | build: 15 | context: ./flask_app 16 | dockerfile: Dockerfile-gevent-gunicorn 17 | environment: 18 | - PORT_APP=3000 19 | - PORT_API=4000 20 | - WORKERS=1 21 | expose: 22 | - "3000" 23 | depends_on: 24 | - slow_api 25 | 26 | slow_api: 27 | init: true 28 | build: ./slow_api 29 | environment: 30 | - PORT=4000 31 | expose: 32 | - "4000" 33 | 34 | -------------------------------------------------------------------------------- /nginx-uwsgi.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | ingress: 4 | image: nginx:1.17.6 5 | ports: 6 | - "127.0.0.1:8080:80" 7 | volumes: 8 | - ./flask_app/nginx-uwsgi.conf:/etc/nginx/conf.d/default.conf 9 | depends_on: 10 | - flask_app 11 | 12 | flask_app: 13 | init: true 14 | build: 15 | context: ./flask_app 16 | dockerfile: Dockerfile-gevent-uwsgi 17 | environment: 18 | - PORT_APP=3000 19 | - PORT_API=4000 20 | - WORKERS=1 21 | - ASYNC_CORES=2000 22 | - PROTOCOL=uwsgi 23 | expose: 24 | - "3000" 25 | depends_on: 26 | - slow_api 27 | 28 | slow_api: 29 | init: true 30 | build: ./slow_api 31 | environment: 32 | - PORT=4000 33 | expose: 34 | - "4000" 35 | 36 | -------------------------------------------------------------------------------- /psycopg2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install Flask requests psycopg2 psycogreen uwsgi gevent 4 | 5 | COPY app.py /app.py 6 | COPY patched.py /patched.py 7 | 8 | CMD uwsgi --master \ 9 | --single-interpreter \ 10 | --workers $WORKERS \ 11 | --gevent $ASYNC_CORES \ 12 | --protocol http \ 13 | --socket 0.0.0.0:$PORT_APP \ 14 | --module $MODULE:app 15 | 16 | -------------------------------------------------------------------------------- /psycopg2/app.py: -------------------------------------------------------------------------------- 1 | from gevent import monkey 2 | monkey.patch_all() 3 | 4 | import os 5 | 6 | import psycopg2 7 | import requests 8 | from flask import Flask, request 9 | 10 | api_port = os.environ['PORT_API'] 11 | api_url = f'http://slow_api:{api_port}/' 12 | 13 | app = Flask(__name__) 14 | 15 | @app.route('/') 16 | def index(): 17 | conn = psycopg2.connect(user="example", password="example", host="postgres") 18 | delay = float(request.args.get('delay') or 1) 19 | resp = requests.get(f'{api_url}?delay={delay/2}') 20 | 21 | cur = conn.cursor() 22 | cur.execute("SELECT NOW(), pg_sleep(%s)", (delay/2,)) 23 | 24 | return 'Hi there! {} {}'.format(resp.text, cur.fetchall()[0]) 25 | 26 | -------------------------------------------------------------------------------- /psycopg2/patched.py: -------------------------------------------------------------------------------- 1 | from psycogreen.gevent import patch_psycopg 2 | patch_psycopg() 3 | 4 | from app import app 5 | 6 | -------------------------------------------------------------------------------- /slow_api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | 3 | RUN pip install aiohttp 4 | 5 | COPY api.py /api.py 6 | 7 | CMD ["python", "/api.py"] 8 | 9 | -------------------------------------------------------------------------------- /slow_api/api.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import asyncio 4 | from aiohttp import web 5 | 6 | async def handle(request): 7 | delay = float(request.query.get('delay') or 1) 8 | await asyncio.sleep(delay) 9 | return web.Response(text='slow api response') 10 | 11 | app = web.Application() 12 | app.add_routes([web.get('/', handle)]) 13 | 14 | if __name__ == '__main__': 15 | web.run_app(app, port=os.environ['PORT']) 16 | 17 | -------------------------------------------------------------------------------- /sync-devserver.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-devserver 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - THREADS=without 12 | ports: 13 | - "127.0.0.1:3000:3000" 14 | depends_on: 15 | - slow_api 16 | 17 | flask_app_threaded: # extends: flask_app 18 | init: true 19 | build: 20 | context: ./flask_app 21 | dockerfile: Dockerfile-devserver 22 | environment: 23 | - PORT_APP=3001 24 | - PORT_API=4000 25 | - THREADS=with 26 | ports: 27 | - "127.0.0.1:3001:3001" 28 | depends_on: 29 | - slow_api 30 | 31 | slow_api: 32 | init: true 33 | build: ./slow_api 34 | environment: 35 | - PORT=4000 36 | expose: 37 | - "4000" 38 | 39 | -------------------------------------------------------------------------------- /sync-gunicorn.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app_gunicorn: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-gunicorn 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - WORKERS=4 12 | - THREADS=50 13 | ports: 14 | - "127.0.0.1:3000:3000" 15 | depends_on: 16 | - slow_api 17 | 18 | slow_api: 19 | init: true 20 | build: ./slow_api 21 | environment: 22 | - PORT=4000 23 | expose: 24 | - "4000" 25 | 26 | -------------------------------------------------------------------------------- /sync-uwsgi.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | flask_app_uwsgi: 4 | init: true 5 | build: 6 | context: ./flask_app 7 | dockerfile: Dockerfile-uwsgi 8 | environment: 9 | - PORT_APP=3000 10 | - PORT_API=4000 11 | - WORKERS=4 12 | - THREADS=50 13 | - PROTOCOL=http 14 | ports: 15 | - "127.0.0.1:3000:3000" 16 | depends_on: 17 | - slow_api 18 | 19 | slow_api: 20 | init: true 21 | build: ./slow_api 22 | environment: 23 | - PORT=4000 24 | expose: 25 | - "4000" 26 | 27 | --------------------------------------------------------------------------------