Gunicorn: Gunicorn doesn't allow requesting urls on the same server instance

Created on 12 Sep 2018  路  2Comments  路  Source: benoitc/gunicorn

Hi, I'm experiencing a strange problem. I have the following simple/sample Flask application:

# from __future__ import print_function
import requests

from flask import Flask

PORT = 6556
app = Flask(__name__)


@app.route('/')
def index():
    j = {'data': 1}

    print('INDEX CALLING API')
    response = requests.post('http://localhost:{}/fake_api'.format(PORT), json=j)
    print('INDEX CALLED API')

    return response.text

@app.route('/fake_api', methods=['POST'])
def fake_api():
    print('FAKE_API CALL RECEIVED')
    return 'Ok, it works'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=PORT, debug=True)

Basically, what I'm simulating is calling a url (http://localhost:6556/) and have the route call with requests another url of the same flask app (http://localhost:6556/fake_api). Being quite banal, it works fine if I simply start the app with python server.py.

The problem is that when I try to run the server with gunicorn like this

gunicorn -b 0.0.0.0:6556 server:app

the application hangs indefinitely on the request. In fact it prints INDEX CALLING API and then halts.

I've experienced the same problem with uWSGI. Any idea?

Most helpful comment

The default type of worker for gunicorn is the sync worker, and the default number of workers is one. sync workers only support a single request at a time:

The most basic and the default worker type is a synchronous worker class that handles a single request at a time. This model is the simplest to reason about as any errors will affect at most a single request.

None of those defaults are being changed here. So you're trying to make a second request, but only one request is supported. Thus gunicorn blocks waiting for the first request to finish before handling the second---which can't happen because the first request is blocked waiting for the second request to finish. You'll need to use a different worker type or number of workers, or use a mechanism supplied by your framework to make an in-app request without spawning a new HTTP socket request (e.g., Pyramid's subrequest).

All 2 comments

The default type of worker for gunicorn is the sync worker, and the default number of workers is one. sync workers only support a single request at a time:

The most basic and the default worker type is a synchronous worker class that handles a single request at a time. This model is the simplest to reason about as any errors will affect at most a single request.

None of those defaults are being changed here. So you're trying to make a second request, but only one request is supported. Thus gunicorn blocks waiting for the first request to finish before handling the second---which can't happen because the first request is blocked waiting for the second request to finish. You'll need to use a different worker type or number of workers, or use a mechanism supplied by your framework to make an in-app request without spawning a new HTTP socket request (e.g., Pyramid's subrequest).

Ok, thanks. I got it working with:

# pip install gevent
gunicorn -b 0.0.0.0:6556 -k gevent server:app
Was this page helpful?
0 / 5 - 0 ratings