我有以下ZMQ脚本
#!/usr/bin/env python2.6
import signal
import sys
import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
def signal_term_handler(signal, fname):
socket.close()
sys.exit(0)
def main():
signal.signal(signal.SIGTERM, signal_term_handler)
socket.connect('tcp://16.160.163.27:8888')
socket.setsockopt(zmq.SUBSCRIBE, '')
print 'Waiting for a message'
while True:
(event, params) = socket.recv().split()
# ... doing something with that data ...
if __name__ == '__main__':
main()
当我Ctrl-C
时,我收到以下错误:
Traceback (most recent call last):
File "./nag.py", line 28, in <module>
main()
File "./nag.py", line 24, in main
(event, params) = socket.recv().split()
File "socket.pyx", line 628, in zmq.backend.cython.socket.Socket.recv (zmq/backend/cython/socket.c:5616)
File "socket.pyx", line 662, in zmq.backend.cython.socket.Socket.recv (zmq/backend/cython/socket.c:5436)
File "socket.pyx", line 139, in zmq.backend.cython.socket._recv_copy (zmq/backend/cython/socket.c:1771)
File "checkrc.pxd", line 11, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:5863)
KeyboardInterrupt
现在,我以为我处理了套接字的关闭,当收到用户的终止信号时,很好,那为什么我会得到这个丑陋的消息。我错过了什么。
注意我在Google和StackOverflow上进行了一些搜索,但还没有找到解决此问题的方法。
感谢。
编辑对于那些已经走到这一步的人 - user3666197提出了一种非常好且强大的方法来处理执行期间的终止或任何异常。
答案 0 :(得分:3)
虽然演示代码很小,但实际系统中,多主机/多进程通信系统通常应处理主控制循环中所有不利影响事件。
try:
context = zmq.Context() # setup central Context instance
socket = ... # instantiate/configure all messaging archetypes
# main control-loop ----------- # ----------------------------------------
#
# your app goes here, incl. all nested event-handling & failure-resilience
# ----------------------------- # ----------------------------------------
except ...:
# # handle IOErrors, context-raised exceptions
except Keyboard Interrupt:
# # handle UI-SIG
except:
# # handle other, exceptions "un-handled" above
finally:
# # GRACEFULL TERMINATION
# .setsockopt( zmq.LINGER, 0 ) # to avoid hanging infinitely
# .close() # .close() for all sockets & devices
#
context.term() # Terminate Context before exit
答案 1 :(得分:1)
使用SIGINT而不是SIGTERM来修复它。
http://www.quora.com/Linux/What-is-the-difference-between-the-SIGINT-and-SIGTERM-signals-in-Linux
答案 2 :(得分:0)
人们可能会想到下面的代码!但是不需要!为了套接字关闭!
套接字自动关闭!
然而,这是手动完成的方式!
此外,我还列出了所有不同的有用信息,以了解有关销毁、关闭或清理主题的含义!
try:
context = zmq.Context()
socket = context.socket(zmq.ROUTER)
socket.bind(SOCKET_PATH)
# ....
finally :
context.destroy() # Or term() for graceful destroy
在进一步之前!错误原因:
Traceback (most recent call last):
File "/usr/lib/python3.9/runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.9/runpy.py", line 87, in _run_code
exec(code, run_globals)
...
msg = self.recv(flags)
File "zmq/backend/cython/socket.pyx", line 781, in zmq.backend.cython.socket.Socket.recv
File "zmq/backend/cython/socket.pyx", line 817, in zmq.backend.cython.socket.Socket.recv
File "zmq/backend/cython/socket.pyx", line 186, in zmq.backend.cython.socket._recv_copy
File "zmq/backend/cython/checkrc.pxd", line 13, in zmq.backend.cython.checkrc._check_rc
KeyboardInterrupt
这只是KeyboardInterrupt错误!
刚抓住它!会解决问题!
例如:
try:
context = zmq.Context()
socket = context.socket(zmq.ROUTER)
socket.bind(SOCKET_PATH)
# ...
except KeyboardInterrupt:
print('> User forced exit!')
错误不再显示!
现在不需要终止上下文了!它会自动完成!
也请注意:如果您没有捕捉到KeyboardInterrupt!只需创建一个 finally:
块并单独运行 context.term()
!该过程将永远挂起!
finally:
socket.close() # assuming one socket within the context
context.term()
或
finally:
context.destroy()
会抛出同样的错误!这证明错误是键盘中断引起的!应该是从图书馆里抓到的!又扔了!
只有捕捉KeyboardInterrupt 才能做到!
except KeyboardInterrupt:
print('> User forced exit!')
finally:
context.destroy() # manual (not needed)
会的!但是添加finally块完全没用!并手动销毁(关闭套接字+终止)
让我告诉你为什么!
如果赶时间,请转到在python中无需在退出时清理部分最后!
来自 zguide:Making-a-Clean-Exit
它声明我们需要关闭所有消息!还有所有的插座!只有在此之前,终止解除阻塞并使代码退出
还有嘭! api 通过 zmq_ctx_destroy()
并关闭套接字并销毁消息!
有很多事情要知道:
<块引用>内存泄漏是一回事,但 ZeroMQ 对退出应用程序的方式非常挑剔。原因是技术性的和痛苦的,但结果是,如果您让任何套接字保持打开状态,zmq_ctx_destroy() 函数将永远挂起。即使您关闭所有套接字,zmq_ctx_destroy() 默认情况下,如果有挂起的连接或发送,将永远等待,除非您在关闭它们之前将这些套接字上的 LINGER 设置为零.
<块引用>我们需要担心的ZeroMQ对象是消息、套接字和上下文< /强>。幸运的是,它非常简单,至少在简单的程序中是这样:
<块引用>在 pyzmq 中! Context.term()
呼叫 zmq_ctx_destroy()
!
另一方面,方法 Context.destroy()
不仅是 zmq_ctx_destroy()
,而且它会关闭上下文的所有套接字!然后调用 Context.term()
调用 zmq_ctx_destroy()
!
来自蟒蛇doc
注意 destroy() 不是 zmq_ctx_destroy()
! term() 是!
destroy() = context socket close() + term()
关闭与此上下文关联的所有套接字,然后终止该上下文。
<块引用>警告 destroy 涉及调用 zmq_close(),这不是线程安全的。如果其他线程中有活动套接字,则不得调用此方法。
<块引用>参数 linger (int, optional) – 如果指定,在关闭套接字之前在套接字上设置 LINGER。
关闭或终止上下文。
<块引用>上下文终止按以下步骤执行:
如果您想手动关闭,这很有用!
这取决于人们可能想要以一种或另一种方式进行的想要的行为!
term()
将为打开套接字操作引发 zmq.ContextTerminated
异常!如果逼出来!可以简单地调用destroy()!为了优雅退出!可以使用term()
!然后在捕获的 zmq.ContextTerminated
异常块中!应该关闭套接字!并做任何处理!要关闭套接字,可以使用 socket.close()!一个接一个地做!我想知道如果我们此时调用 destroy() 会发生什么!它可能有效!套接字将关闭!但是随后对 context.term() 的第二次调用将会执行!可能没问题!可能不会!没试过!
勾选ZMQ_LINGER: Set linger period for socket shutdown 标题! (ctrl + f)
http://api.zeromq.org/2-1:zmq-setsockopt
<块引用>ZMQ_LINGER 选项应设置指定套接字的延迟时间。延迟周期决定了在使用 zmq_close(3) 关闭套接字后,尚未发送到对等方的未决消息将在内存中停留多长时间,并进一步影响使用 zmq_term(3) 终止套接字上下文。以下概述了不同的行为:
<块引用>选项值类型:int
选项值单位:毫秒
默认值:-1(无限)
适用插座类型:所有
如果您想手动销毁上下文,您只能使用 destroy() 或 term() 和 destroy() 的组合!如果您想对 zmq.ContextTerminated
异常进行一些处理!或者在处理多个上下文时!而您正在创建它们并关闭它们!尽管通常我们从不这样做!或者代码运行正常时的一些原因!
否则如 zguide
中所述 <块引用>至少对于 C 开发来说是这样。 在具有自动对象销毁功能的语言中,套接字和上下文将在您离开作用域时被销毁。如果您使用异常,则必须在“最终”块中进行清理,与任何资源相同。
您可以在上面 Context.term()
的 pyzmq 文档中看到它:
可以调用这个来手动关闭上下文。如果不调用此方法,则上下文将在垃圾收集时自动关闭。
当变量超出范围时,它们会被销毁!并且销毁和退出将自动处理!当程序退出时!让我们说即使在最终代码之后!所有变量都会被销毁!所以清洁工作会在那里进行!
再来一次!如果您遇到一些问题!确保它不是上下文、套接字和消息关闭相关!并确保使用最新版本的pyzmq