Исключения ZeroRPC Python Server при попытке остановить или закрыть

У меня есть немного больше кода, чем это, поэтому я сокращаю его до того, что кажется уместным. Согласно задокументированному примеру, у меня есть класс python для использования ZeroRPC:

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. Когда время соединения клиента истекает или сервер отключается, я вызываю 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() очищает работающий сервер (и его файл сокета в каталоге) без каких-либо исключений или предупреждений.

Все это оставляет меня с вопросом: как можно чисто остановить сервер ZeroRPC в Python, если не с помощью stop() или close()?

1 ответ

Решение

Используйте gevent.signal(), чтобы установить ваш обработчик сигнала вместо signal.signal().

Это связано с тем, что стандартный сигнал модуля напрямую отображает API-интерфейс UNIX, который запускает обработчик сигнала вне gevent eventloop, без какой-либо концепции gevent greenlet/coroutine. gevent.signal интегрирован с Gevent ioloop, и убедитесь, что ваш обработчик сигнала выполняется в его собственном greenlet, в EventLop.

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()
Другие вопросы по тегам