11

I have a Django app. It uses Nginx, sits on an EC2 instance, and uses an AWS load balancer in front of that. Nginx serves static files right from disk as well as using proxy_pass for everything else to route to the WSGI app server.

It is the choice of WSGI server that I'm seeing puzzling results for.

When I use Django's development server, ./manage.py runserver 0.0.0.0:8000, the average response time for the resulting HTML document at / is 60-70 ms:

Blocked: 1 ms
DNS resolution: 0 ms
Connecting: 0 ms
TLS setup: 0 ms
Sending: 0 ms
Waiting: 68 ms
Receiving: 0 ms

Now when I replace this with a "proper" Gunicorn server, the average response time for the same request/doc is now 1800 ms!

Blocked: 1 ms
DNS resolution: 0 ms
Connecting: 0 ms
TLS setup: 0 ms
Sending: 0 ms
Waiting: 1859 ms
Receiving: 0 ms

Here I'm running Gunicorn as:

gunicorn \
    --log-level DEBUG \
    --bind 0.0.0.0:8000 \
    --workers 4 \
    project.wsgi

The same exact thing goes for uwsgi, with an average reponse time of 1850 ms:

uwsgi --chdir=/home/ubuntu/project \
    --module=project.wsgi:application \
    --env DJANGO_SETTINGS_MODULE=project.settings \
    --master \
    --http-socket 0.0.0.0:8000 \
    --processes=5 \
    --max-requests=5000 \
    --vacuum

And, notably, the response time does not change regardless of whether the number of workers is much higher e.g. --workers 64 (nor would I expect it to, since this is not about concurrent requests).

What would likely account for this difference? Am I falling for some common pitfall?

Versions:

$ python -m django --version
2.2.6
$ gunicorn --version
gunicorn (version 19.9.0)
$ nginx -v
nginx version: nginx/1.14.0 (Ubuntu)
Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
  • 2
    Could this be due to `WORKERCLASS` type for gunicorn? I think by default it is `sync`. http://docs.gunicorn.org/en/stable/settings.html#worker-class – brokenfoot Oct 30 '19 at 21:24
  • What do you mean @brokenfoot? I see that the [default is sync](https://docs.gunicorn.org/en/stable/settings.html#worker-class), but I've never seen it noted anywhere that just sticking with default will degrade performance – Brad Solomon Oct 30 '19 at 21:26
  • Somtime back, I ran a flask web server and saw same issues. Then i switched the worked type to any of async options, it made the difference. You can try out `gunicorn --log-level DEBUG --bind 0.0.0.0:8000 --workers 4 -k "gevent" project.wsgi`, just to see if it speeds up. – brokenfoot Oct 30 '19 at 21:28
  • Ok - interesting, I'm seeing ~1800 ms with uwsgi too – Brad Solomon Oct 30 '19 at 21:34
  • I meant try `gunicorn` with any async worker type. `uwsgi` seems to be an alternative for `gunicorn`. – brokenfoot Oct 30 '19 at 21:51
  • Does the response time reduce over time as python code gets compiled/cached for uWSGI? Perhaps the other methods are doing some pre-caching or something? – DanH Aug 24 '20 at 11:20
  • for serving static files you can just use `nginx` directly, in theory it should be faster because it skips the request `proxy_pass` to server – ofirule Sep 11 '20 at 17:13
  • True @ofirule, though in this question, by "HTML document", I implicitly meant one containing data that is rendered dynamically by Django/Flask/Jinja – Brad Solomon Sep 11 '20 at 18:13
  • you can dynamically scale workers using cheaper algorithms might helpful. https://uwsgi-docs.readthedocs.io/en/latest/Cheaper.html – Ajay K Nov 24 '22 at 12:06
  • How much RAM and CPU does your EC2 instance have? – Özer Dec 06 '22 at 11:46
  • On an aws instance pointed from nginx the server doesnt have to be started on localhost with the command so you could have the default instance running on top of whatever you started with gunicorn there running its own python applications. Show command 'netstat -tulpn' and this issue should be forthcoming, 'fuser -k port/tcp' and test. – Gracen Ownby Apr 16 '23 at 20:28

0 Answers0