Mansour
Mansour

Reputation: 1810

gevent (py)wsgi graceful shutdown

The only way I know (from searching and inspecting the gevent's source) to gracefully shutdown a gevent WSGI based server is:

server = gevent.wsgi.WSGIServer(('', 80), someWSGIApp)
def shutdown():
  print('Shutting down ...')
  server.stop(timeout=60)
  exit(signal.SIGTERM)
gevent.signal(signal.SIGTERM, shutdown)
server.serve_forever()

Now, what I mean by graceful is to wait for all the greenlets to terminate by themselves. So for instance if they're still serving requests, they can finish them up properly.

Problem is, with the above seemingly correct code, the server does indeed wait for max. of 60 seconds, but all the TCP connections are terminated immediately upon receiving SIGTERM. Greenlets however continue doing what they were (e.g. sleeping) until either they finish or the timeout occurs.

Any ideas?

Upvotes: 4

Views: 5911

Answers (3)

Tyrion
Tyrion

Reputation: 485

You can solve the problem by running the server in one thread and shut it down in another. The code below is running in Python 3.7.

from gevent.pywsgi import WSGIServer
import signal
import threading

# define your app here
app = ...

server_address = ("localhost", 4000)


class WebServer(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        global server
        server = WSGIServer(server_address, app)
        server.serve_forever()


def shutdown(num, info):
    print(f'Shutting down website server...\n'
          f'{num} {info}')
    server.stop()
    server.close()


if __name__ == "__main__":
    server = None
    WebServer().start()

    signal.signal(signal.SIGINT, shutdown)

Upvotes: 2

Michal Charemza
Michal Charemza

Reputation: 27012

but all the TCP connections are terminated immediately upon receiving SIGTERM.

I had a similar, but not identical, issue...

... my issue was that the Python process would then exit even if connections were still in-progress. I solved this by adding a gevent.get_hub().join() after server.serve_forever()

server = gevent.wsgi.WSGIServer(('', 80), someWSGIApp)
gevent.signal(signal.SIGTERM, server.stop)
server.serve_forever()
gevent.get_hub().join()

Upvotes: 0

Tommaso Barbugli
Tommaso Barbugli

Reputation: 12031

As docstring says in server's stop method (gevent.baseserver.BaseServer:stop)

Stop accepting the connections and close the listening socket.

If the server uses a pool to spawn the requests, then :meth:`stop` also 
for all the handlers to exit. 
If there are still handlers executing after *has expired (default 1 second), 
then the currently running handlers in the pool are killed.

I did not try this but if docs are correct you should be able to get the graceful stop with something like this:

from gevent.pool import Pool

pool_size = 8
worker_pool = Pool(pool_size)
gevent.wsgi.WSGIServer(('', 80), someWSGIApp, spawn=worker_pool)

Upvotes: 1

Related Questions