Search code examples
pythonsocketssignalszeromq

ZMQ socket gracefully termination in Python


I have the following ZMQ script

#!/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()

When I Ctrl-C, I get the following errors:

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

Now, I thought I handled the closing of the socket, when receiving a termination signal from the user, pretty well, then why do I get this ugly messages. What am I missing.

Note I have done some search on Google and StackOverflow but haven't found anything that fixes this problem.

Thanks.

EDIT To anyone that has gotten this far -- user3666197 has suggested a very-good-and-robust way to handle termination or any exception during the execution.


Solution

  • Event handling approach

    While the demo-code is small, the real-world systems, the more the multi-host / multi-process communicating systems, shall typically handle all adversely impacting events in their main control-loop.

    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 KeyboardInterrupt:
        #                               # 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