Исключения 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()