简体   繁体   中英

Gunicorn with gevent does not enforce timeout

Let's say I have a simple flask app:

import time
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    for i in range(10):
        print(f"Slept for {i + 1}/{seconds} seconds")
        time.sleep(1)
    return "Hello world"

I can run it with gunicorn with a 5 second timeout:

gunicorn app:app -b 127.0.0.1:5000 -t 5

As expected, http://127.0.0.1:5000 times out after 5 seconds:

Slept for 1/10 seconds
Slept for 2/10 seconds
Slept for 3/10 seconds
Slept for 4/10 seconds
Slept for 5/10 seconds
[2022-07-07 22:45:01 -0700] [57177] [CRITICAL] WORKER TIMEOUT (pid:57196)

Now, I want to run gunicorn with an async worker to allow the web server to use its available resources more efficiently, maximizing time that otherwise would be spent idling to do additional work instead. I'm using gevent, still with a timeout of 5 seconds.

gunicorn app:app -b 127.0.0.1:5000 -t 5 -k gevent

Unexpectedly, http://127.0.0.1:5000 does NOT time out:

Slept for 1/10 seconds
Slept for 2/10 seconds
Slept for 3/10 seconds
Slept for 4/10 seconds
Slept for 5/10 seconds
Slept for 6/10 seconds
Slept for 7/10 seconds
Slept for 8/10 seconds
Slept for 9/10 seconds
Slept for 10/10 seconds

Looks like this is a known issue with gunicorn. The timeout only applies to the default sync worker, not async workers: https://github.com/benoitc/gunicorn/issues/2695


uWSGI is an alternate option to gunicorn. I'm not as familiar with it. Looks like its timeout option is called harakiri and it can be run with gevent:

uwsgi --http 127.0.0.1:5000 --harakiri 5 --master -w app:app --gevent 100

uWSGI's timeout sometimes works as expected with gevent:

Slept for 1/10 seconds
Slept for 2/10 seconds
Slept for 3/10 seconds
Slept for 4/10 seconds
Slept for 5/10 seconds
Thu Jul  7 23:20:59 2022 - *** HARAKIRI ON WORKER 1 (pid: 59836, try: 1) ***
Thu Jul  7 23:20:59 2022 - HARAKIRI !!! worker 1 status !!!
Thu Jul  7 23:20:59 2022 - HARAKIRI [core 99] 127.0.0.1 - GET / since 1657261253
Thu Jul  7 23:20:59 2022 - HARAKIRI !!! end of worker 1 status !!!
DAMN ! worker 1 (pid: 59836) died, killed by signal 9 :( trying respawn ...

But other times it doesn't time out so it appears to be pretty flaky.


Is there anyway to enforce a timeout using gunicorn with an async worker? If not, are there any other web servers that enforce a consistent timeout with an async worker, similar to uWSGI?

From https://docs.gunicorn.org/en/stable/settings.html#timeout :

Workers silent for more than this many seconds are killed and restarted.

For the non sync workers it just means that the worker process is still communicating and is not tied to the length of time required to handle a single request.

So timeout is likely functioning by design — as worker timeout, not request timeout.

You can subclass GeventWorker to override handle_request() with gevent.Timeout :

import gevent
from gunicorn.workers.ggevent import GeventWorker


class MyGeventWorker(GeventWorker):

    def handle_request(self, listener_name, req, sock, addr):
        with gevent.Timeout(self.cfg.timeout):
            super().handle_request(listener_name, req, sock, addr)

Usage:

# gunicorn app:app -b 127.0.0.1:5000 -t 5 -k gevent
gunicorn app:app -b 127.0.0.1:5000 -t 5 -k app.MyGeventWorker

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM