2

我有比这更多的代码,所以我将它精简到似乎相关的内容。根据记录的示例,我有一个供 ZeroRPC 使用的 python 类:

import zerorpc, sys, signal

class MyClass:
    pass

zpc = 0
if __name == '__main__':
    zpc = zerorpc.Server(MyClass)
    zpc.bind('ipc://./mysocket.sock')
    zpc.run()
    print("zpc stopped"); sys.stdout.flush()

python 脚本作为 ChildProcess 从我的 Node.js 服务器生成,该服务器侦听 stdout 和 stderr。当客户端连接超时或服务器关闭时,我在 ChildProcess 上调用 kill() ,该 ChildProcess 将 SIGTERM 发送给它。

仅使用上面的代码,“zpc 停止”永远不会在 Node.js 回调中被捕获,这向我表明 ZeroRPC 服务器在其运行循环的某个地方被杀死。此外,套接字文件仍然存在,表明服务器也没有关闭套接字。所以我想我会在捕获 SIGTERM 后在服务器上调用 stop() 或 close() :

def sig_handle (signal, frame):
    global zpc
    print("SIGTERM received.") # <-- this does occur
    zpc.stop() # <-- Exception thrown here and at run()
    sys.exit(0)

signal.signal(signal.SIGTERM, sig_handle)

Node.js 通过其 stderr 回调获取异常:

Gateway Error:   File "/usr/lib/python2.6/site-packages/zerorpc/core.py", line 178, in stop

Gateway Error:     self._acceptor_task.kill()
  File "/usr/lib64/python2.6/site-packages/gevent/greenlet.py", line 235, in kill

Gateway Error:     waiter.get()
  File "/usr/lib64/python2.6/site-packages/gevent/hub.py", line 568, in get

Gateway Error:     return self.hub.switch()
  File "/usr/lib64/python2.6/site-packages/gevent/hub.py", line 330, in switch
    switch_out()
  File "/usr/lib64/python2.6/site-packages/gevent/hub.py", line 334, in switch_out
    raise AssertionError('Impossible to call blocking function in the event loop callback')
AssertionError: Impossible to call blocking function in the event loop callback

Gateway Error: Traceback (most recent call last):
  File "gateway.py", line 111, in <module>
    zpc.run() 

Gateway Error:   File "/usr/lib/python2.6/site-packages/zerorpc/core.py", line 171, in run
    self._acceptor_task.get()
  File "/usr/lib64/python2.6/site-packages/gevent/greenlet.py", line 258, in get

Gateway Error:     result = self.parent.switch()
  File "/usr/lib64/python2.6/site-packages/gevent/hub.py", line 331, in switch

Gateway Error:     return greenlet.switch(self)
AssertionError: Impossible to call blocking function in the event loop callback

将 stop() 更改为 close() 会导致相同的最终异常集。在 Javascript (Node.js) 中实现相同的想法,close() 清理正在运行的服务器(及其目录中的套接字文件)而不抛出任何异常或警告。

这一切都给我留下了一个问题:如果不是通过 stop() 或 close(),如何在 Python 中干净地停止 ZeroRPC 服务器?

4

1 回答 1

6

使用 gevent.signal() 而不是 signal.signal() 来设置您的信号处理程序。

这是因为标准模块 signal 直接映射 UNIX API,它在 gevent eventloop 之外运行信号处理程序,没有任何 gevent greenlet/coroutine 的概念。gevent.signal 与 gevent ioloop 集成,并确保在 eventloop 中在其自己的 greenlet 中执行您的信号处理程序。

Gevent 兼容解决方案:

import zerorpc, sys, gevent, signal, os

class MyClass:
      pass

if __name__ == '__main__':
  zpc = zerorpc.Server(MyClass)
  zpc.bind('ipc://./mysocket.sock')
  gevent.signal(signal.SIGTERM, zpc.stop)
  zpc.run()
  print("zpc stopped"); sys.stdout.flush()
于 2014-01-15T19:53:26.183 回答