public
Last active

Graceful tornado http server shutdown

  • Download Gist
graceful_http_server.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
class MyHTTPServer(tornado.httpserver.HTTPServer):
_stopped = False
 
def _quit_if_ioloop_is_empty(self):
ioloop = tornado.ioloop.IOLoop.instance()
if len(ioloop._handlers) <= 1:
logger.info("Graceful shutdown complete. Exiting!")
exit(0)
else:
logger.info("Waiting for ioloop to be empty. has %d handlers left" % len(ioloop._handlers))
def _setup_worker_handlers(self):
def quick_shutdown_handler(signum, frame):
logger.warning("worker received signal %d. exiting immediately" % signum)
exit(0)
 
def graceful_shutdown_handler(signum, frame):
logger.warning("worker received signal %d. gracefully shutting down" % signum)
if not self._stopped:
self.no_keep_alive = True
self._stopped = True
self.stop()
max_wait_seconds = 100
logger.info("Waiting for all connections to finish or %d seconds to pass (setting SIGALRM)" % max_wait_seconds)
signal.signal(signal.SIGALRM, quick_shutdown_handler)
signal.alarm(max_wait_seconds)
 
pc = tornado.ioloop.PeriodicCallback(self._quit_if_ioloop_is_empty, 100, io_loop=tornado.ioloop.IOLoop.instance())
pc.start()
else:
logger.warning("worker received signal %d. graceful shutdown has already begun" % signum)
 
signal.signal(signal.SIGINT, quick_shutdown_handler)
signal.signal(signal.SIGTERM, quick_shutdown_handler)
signal.signal(signal.SIGQUIT, graceful_shutdown_handler)
 
def _setup_master_handlers(self):
def quick_shutdown_handler(signum, frame):
logger.warning("master received signal %d. Shutting down worker quickly" % signum)
if self.child_pid:
os.kill(self.child_pid, signum)
os.waitpid(self.child_pid, 0)
logger.warning("Child shut down successfully")
 
def graceful_shutdown_handler(signum, frame):
logger.warning("master received signal %d. Shutting down worker gracefully" % signum)
if self.child_pid:
os.kill(self.child_pid, signum)
logger.warning("master process exiting and detatching child")
exit(0)
 
signal.signal(signal.SIGINT, quick_shutdown_handler)
signal.signal(signal.SIGTERM, quick_shutdown_handler)
signal.signal(signal.SIGQUIT, graceful_shutdown_handler)
 
def start(self, num_processes=1):
if num_processes != 1:
logger.error("We can only support one process. Setting to 1")
num_processes = 1
 
self.child_pid = os.fork()
if not self.child_pid:
self._setup_worker_handlers()
tornado.httpserver.HTTPServer.start(self, num_processes)
else:
self._setup_master_handlers()
logger.info("Waiting on child pid %d" % self.child_pid)
try:
os.waitpid(self.child_pid, 0)
logger.info("Child process appears to have exited")
self.child_pid = None
except OSError, e:
if e.errno != 4:
raise
finally:
exit(0)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.