TProcessPoolServer正常关机?

时间:2012-05-21 17:29:43

标签: python thrift

你如何优雅地关闭python thrift服务器,TProcessPoolServer?我还没有找到任何文档,示例或博客文章。到目前为止,我的经历如下。

我正在命令行上运行我的thrift服务器./thrift_service.py,而不是在主管下。我正在使用python 2.6和thrift 0.8.0。

我最初尝试过:

server = TProcessPoolServer(processor, transport, tfactory, pfactory)
try:
    server.serve()
finally:
    server.stop()

当我发送sigterm父python进程时,我在输出中看到“Terminated”,进程被终止,但是它的子进程被孤立并继续运行。

然后我偶然发现了thrift server tests,并尝试了:

import signal
def set_alarm(server):
    def clean_shutdown(signum, frame):
        for worker in server.workers:
            logging.error("Terminating worker: {0}".format(worker))
            worker.terminate()
        logging.error("Requesting server to stop()")
        try:
            server.stop()
        except (KeyboardInterrupt, SystemExit):
            pass
        except Exception as err:
            logging.exception(err)
    def logme(s, *args, **kwargs):
        logging.error(">>> {0} <<<".format(s))
        clean_shutdown(*args, **kwargs)
    signal.signal(signal.SIGALRM, clean_shutdown)
    signal.signal(signal.SIGHUP, clean_shutdown)
    signal.signal(signal.SIGINT, clean_shutdown)
    signal.signal(signal.SIGTERM, lambda x, y: logme("SIGTERM", x, y))
server = TProcessPoolServer(processor, transport, tfactory, pfactory)
set_alarm(server)
server.serve()

当我向父python进程发送sigterm,sigalrm,sighup或sigint时,服务器停止接受连接,但进程不会终止。

在输出中我看到:

ERROR:root:>>> SIGTERM <<<
ERROR:root:Terminating worker: <Process(Process-1, started daemon)>
ERROR:root:Terminating worker: <Process(Process-2, started daemon)>
ERROR:root:Terminating worker: <Process(Process-3, started daemon)>
ERROR:root:Terminating worker: <Process(Process-4, started daemon)>
ERROR:root:Terminating worker: <Process(Process-5, started daemon)>
ERROR:root:Requesting server to stop()

这是预期的,但随后再次捕获信号,进程不再处于启动状态,并要求服务器停止。这部分发生了大约十次,然后没有更多的输出。

ERROR:root:>>> SIGTERM <<<
ERROR:root:Terminating worker: <Process(Process-1, unknown daemon)>
ERROR:root:Requesting server to stop()

有时候,我会在多处理库中看到一个AssertionError:

Traceback (most recent call last):
  File "/path/to/thrift_service.py", line 340, in clean_shutdown
    server.stop()
  File "/usr/local/lib/python2.6/dist-packages/thrift/server/TProcessPoolServer.py", line 123, in stop
    self.stopCondition.notify()
  File "/usr/lib/python2.6/multiprocessing/synchronize.py", line 223, in notify
    assert not self._wait_semaphore.acquire(False)
AssertionError

2 个答案:

答案 0 :(得分:2)

我使用信号和它公开的postForkCallback为python中的TProcessPoolServer添加了一个正常关闭。初始化后,TProcessPoolServer将在每个工作进程中调用postForkCallback。这允许您设置信号处理程序并正常关闭。由于worker捕获了SystemExit或KeyboardInterruptException异常,因此可以为SIGINT设置处理程序,然后在完成清理调用sys.exit(0)之后,这将导致worker关闭。

import signal
import sys

def setupHandlers():
    signal.signal(signal.SIGINT, handleSIGINT)
    #Optionally if you want to keep the current socket connection open and working
    #tell python to make system calls non-interruptable, which is probably what you want.
    signal.siginterrupt(signal.SIGINT, False)

def handleSIGINT(sig, frame):
     #clean up state or what ever is necessary
     sys.exit(0)

server = TProcessPoolServer(processor, transport, tfactory, pfactory)
server.setPostForkCallback(setupHandlers)

#Setup handlers in main process too
setupHandlers()

#Start server
server.start()

这样生成的每个工作进程都会设置信号处理程序以正确处理正常关闭。在这个例子中,我为主进程设置了相同的处理程序以及可能根据你的用例工作的worker,但如果需要,你可以轻松地为主进程定义一个不同的处理程序。请记住,处理程序将从每个进程的上下文中调用,因此在清理期间您将无法跨进程共享状态。

请参阅http://docs.python.org/library/signal.html,详细了解signal.siginterrupt的作用以及您可能需要它的原因。

编辑:您需要使用Crtl + C将SIGINT信号发送到所有进程,或者它是否作为守护程序kill -SIGINT [所有进程的pids]运行

你可以使用ps --ppid [parent pid]

轻松获得工人的pids

答案 1 :(得分:0)

程序启动后,我记录了主进程的进程号。然后根据ps --ppid,取回主进程的子进程,一一kill掉。

我的服务的控制shell脚本代码:

function stop
{
    SERVER_PID=`cat logs/server.pid`
    SPIDS=`ps --ppid $SERVER_PID | awk '{if ($1!="PID") print $1}'`
    kill -9 $SERVER_PID
    for PID in $SPIDS
    do
        kill -9 $PID
    done
}