Python脚本未收到监控程序发送的退出信号

2 投票
1 回答
1790 浏览
提问于 2025-04-18 16:59

我正在运行一个Python脚本,这个脚本创建了一个Tornado服务器,而这个服务器是通过supervisor来管理的。每当我执行supervisorctl reload命令(通常是在部署之后),我希望能优雅地关闭所有的WebSocket客户端连接。

我遇到的问题是,当supervisor杀掉我的服务器时,我无法调用一个特定的函数,但如果我用kill命令发送信号,或者在控制台运行并用Control+C结束进程时,这个函数就能正常工作。我尝试了其他信号和配置,但都没有成功。

import signal, sys

def clean_resources(signum, frame):
    print "SIG: %d, clean me" % signum
    sys.exit(0)

if __name__ == '__main__':

    # Nicely handle closing the server
    for sig in (signal.SIGINT, signal.SIGTERM):
        signal.signal(sig, clean_resources)

这是我的tornado_supervisor.conf配置文件

[program:tornado_server]
command = python /opt/tornado/server.py -p 8890
user = www-data
stdout_logfile = /var/log/tornado/tornado_server_sup.log
redirect_stderr = true
autorestart=true
environment=HOME='/var/www'
environment=PYTHONPATH="$PYTHONPATH:/opt/tornado/"
stopsignal = TERM
stopwaitsecs = 10
stopasgroup = true

1 个回答

0

我遇到过类似的问题。只有父进程收到了信号,而子进程却没有被杀掉。

我做了一些调整,让父进程手动杀掉子进程,使用了 os.killpg() 这个方法。同时,子进程在被杀之前会有一些延迟,以便(可能)完成当前的请求:

#will be initialized in main()
server = None
loop = None

def stop_loop():
    global loop
    loop.stop()

def signal_handler_child_callback():
    global loop
    global server
    server.stop()
    # allow to finish processing current requests
    loop.add_timeout(time.time() + LOOP_STOP_DELAY, stop_loop)

def signal_handler(signum, frame):
    global loop
    global server
    if loop:
        #this is child process, will restrict incoming connections and stop ioloop after delay
        loop.add_callback(signal_handler_child_callback)
    else:
        #this is master process, should restrict new incomming connections
        #and send signal to child processes
        server.stop()
        signal.signal(signal.SIGTERM, signal.SIG_DFL)
        os.killpg(0, signal.SIGTERM)


def main():
  parse_command_line()      
  signal.signal(signal.SIGTERM, signal_handler)

  # ...

  tornado_app = tornado.web.Application(
      [
          #...
      ])

  global server 
  server = tornado.httpserver.HTTPServer(tornado_app)
  server.bind(options.port)  
  server.start(0)
  global loop
  loop = tornado.ioloop.IOLoop.instance()
  loop.start()

if __name__ == '__main__':
  main()

撰写回答