如何正确完全停止pyzmq中的ioloop以释放套接字?

时间:2014-08-07 14:04:29

标签: python tornado zeromq pyzmq

我试图在python中创建zmq的高级接口 这是它的样子

from abc import ABCMeta, abstractmethod
import zmq
from abstracts.BaseMessagingNode import BaseMessagingNode

__author__ = 'michael'


class BaseZmqNode():
    __metaclass__ = ABCMeta

    def __init__(self, host, port,  bind, hwm):
        self.node = self.create_node()
        self.node.host = host
        self.port = port
        self.context = zmq.Context().instance()
        self.socket = self.create_socket()
        if bind:
            self.socket.bind(self.build_address(host, port))
        else:
            self.socket.connect(self.build_address(host, port))
        self.set_hwm(hwm)

    def set_hwm(self, hwm):
        self.socket.set_hwm(hwm)

    def send_multipart(self, message):
        self.socket.send_multipart(message)

    def send_json(self, json):
        self.socket.send_json(json)

    def create_node(self):
        return BaseMessagingNode

    def close(self):
        self.socket.close()

    @staticmethod
    def build_address(host, port):
        strings = [host, ':', str(port)]
        return ''.join(strings)

    @abstractmethod
    def create_socket(self):
        pass

from abc import ABCMeta
from zeromq.ZmqLoopRunner import ZmqLoopRunner
from zmq.eventloop.zmqstream import ZMQStream
from abstracts.ReceivingNode import ReceivingNode
from zeromq.BaseZmqNode import BaseZmqNode

__author__ = 'michael'

class BaseZmqReceiver(BaseZmqNode):

    __metaclass__ = ABCMeta

    def __init__(self, host, port, hwm, bind, on_receive_callback):
        super(BaseZmqReceiver, self).__init__(host=host, port=port, bind=bind, hwm=hwm)
        self.node.on_message_callback = on_receive_callback
        self.stream = ZMQStream(self.socket)
        self.stream.on_recv(self.on_message_received)
        self.runner = ZmqLoopRunner()
        self.runner.start()

    def on_message_received(self, message):
        return self.node.on_message_callback(message)

    def create_node(self):
        return ReceivingNode(None, None)

    def close(self):
        # super(BaseZmqReceiver, self).close()

        self.runner.stop()
        self.stream.close()
        # self.socket.close()


from threading import Thread
from zmq.eventloop import IOLoop

__author__ = 'michael'
class ZmqLoopRunner(Thread):

    def __init__(self):
        super(ZmqLoopRunner, self).__init__()
        self.loop = IOLoop.current()

    def run(self):
        self.loop.start()

    def stop(self):
        self.loop.add_callback(self.loop.stop)

所以基本上我试图在单独的线程中运行ioloop,因此它不会阻止使用zmq进行通信的应用程序。

我有一套测试,全部通过我的代码。但在三分之一的运行中,我有这个例外。我甚至不知道从哪里开始......

 File "/opt/leos/code/messaging_system/zeromq/ZmqLoopRunner.py", line 12, in run
    self.loop.start()
  File "/Library/Python/2.7/site-packages/zmq/eventloop/ioloop.py", line 160, in start
    super(ZMQIOLoop, self).start()
  File "/Library/Python/2.7/site-packages/tornado/ioloop.py", line 646, in start

    event_pairs = self._impl.poll(poll_timeout)
  File "/Library/Python/2.7/site-packages/zmq/eventloop/ioloop.py", line 132, in poll
    z_events = self._poller.poll(1000*timeout)

  File "/Library/Python/2.7/site-packages/zmq/sugar/poll.py", line 110, in poll
    return zmq_poll(self.sockets, timeout=timeout)
  File "_poll.pyx", line 125, in zmq.backend.cython._poll.zmq_poll (zmq/backend/cython/_poll.c:1705)
IndexError: list index out of range

我认为问题在于套接字和流关闭时循环没有完全关闭。

1 个答案:

答案 0 :(得分:1)

根据docs,有两种方法可以启动IOLoop

  1. 在开始循环之前和创建make_current()以设置正确的实例之后显式调用self.loop
  2. 或者,使用IOLoop.instance()而不是使用IOLoop.current()来获取有效的实例。这将确保您拥有IOLoop
  3. 的实例

    注意:我没有尝试过,但怀疑这是问题所在。希望这有帮助!