zeromq:如何防止无限等待?


73

我刚开始使用ZMQ。我正在设计一个工作流程为:

  1. 在5555的多个客户端之一(具有随机的PULL地址)向服务器推送请求
  2. 服务器永远在等待客户端推送。当一个请求到达时,将为该特定请求生成一个工作进程。是的,工作进程可以同时存在。
  3. 该流程完成任务后,会将结果推送给客户端。

我认为PUSH / PULL体系结构适合于此。请对此进行指正


但是,如何处理这些情况?

  1. 服务器无法响应时,client_receiver.recv()将无限期等待。
  2. 客户端可以发送请求,但是此请求将立即失败,因此工作进程将永远停留在server_sender.send()处。

那么如何在PUSH / PULL模型中设置超时等内容?


编辑:感谢user938949的建议,我得到了一个有效的答案,我在后人中分享。


1
我不是0mq专家,但在很多情况下,最好在启动时创建工作人员池,而不是在响应消息时创建工作人员。也许我误会了你。
wberry 2011年

好点子。我实际上打算预分工。我只是意识到,使用0mq可能是微不足道的。
Jesvin Jose 2011年

Answers:


79

如果使用zeromq> = 3.0,则可以设置RCVTIMEO套接字选项:

client_receiver.RCVTIMEO = 1000 # in milliseconds

但通常,您可以使用轮询器:

poller = zmq.Poller()
poller.register(client_receiver, zmq.POLLIN) # POLLIN for recv, POLLOUT for send

poller.poll()超时:

evts = poller.poll(1000) # wait *up to* one second for a message to arrive.

evts 如果什么也收不到,将是一个空列表。

您可以使用轮询zmq.POLLOUT,以检查发送是否成功。

或者,要处理可能失败的对等方,请执行以下操作:

worker.send(msg, zmq.NOBLOCK)

可能就足够了,它将始终立即返回-如果发送无法完成,则引发ZMQError(zmq.EAGAIN)。


您能详细介绍一下zmq.NOBLOCK吗?
Jesvin Jose 2011年

嗨,每次断开连接并重新连接时,我们是否都必须重新注册一个套接字(在轮询器中)?
mariolpantunes,2014年

不,仅当您关闭插座并打开一个新插座时,才需要重新注册。
明斯克2014年

3
就像@Adobri和@mknaf在下面说的那样:如果使用zmq.RCVTIMEO,则还需要设置zmq.LINGER,否则即使超时后套接字仍然无法关闭。在Python中,它是socket.setsockopt(zmq.RCVTIMEO, 1000) socket.setsockopt(zmq.LINGER, 0) message = socket.recv()
dthor

:这两条线在python工作results_receiver.RCVTIMEO = 1000results_receiver.setsockopt(zmq.RCVTIMEO, 1000)
silgon

15

这是我参考user938949的答案和http://taotetek.wordpress.com/2011/02/02/python-multiprocessing-with-zeromq/之后进行的快速修改。如果您做得更好,请发表您的答案,我会推荐您的答案

对于那些想要持久的可靠性解决方案的人,请参见http://zguide.zeromq.org/page:all#toc64

zeromq(beta ATM)的3.0版支持ZMQ_RCVTIMEO和ZMQ_SNDTIMEO中的超时http://api.zeromq.org/3-0:zmq-setsockopt

服务器

zmq.NOBLOCK确保当客户端不存在时,send()不会阻塞。

import time
import zmq
context = zmq.Context()

ventilator_send = context.socket(zmq.PUSH)
ventilator_send.bind("tcp://127.0.0.1:5557")

i=0

while True:
    i=i+1
    time.sleep(0.5)
    print ">>sending message ",i
    try:
        ventilator_send.send(repr(i),zmq.NOBLOCK)
        print "  succeed"
    except:
        print "  failed"

客户

轮询对象可以在许多recieving插座听(见“Python的多处理ZeroMQ”之上。联系我联系只在work_receiver。在无限循环,客户端投票与1000毫秒的时间间隔,袜子对象返回空,如果没有当时已收到该消息。

import time
import zmq
context = zmq.Context()

work_receiver = context.socket(zmq.PULL)
work_receiver.connect("tcp://127.0.0.1:5557")

poller = zmq.Poller()
poller.register(work_receiver, zmq.POLLIN)

# Loop and accept messages from both channels, acting accordingly
while True:
    socks = dict(poller.poll(1000))
    if socks:
        if socks.get(work_receiver) == zmq.POLLIN:
            print "got message ",work_receiver.recv(zmq.NOBLOCK)
    else:
        print "error: message timeout"

Python有select吗?Ruby的库有一个like IO.select。您可以:if ZMQ.select([read_socket], nil, nil, 20); puts read_socket.recv; else; puts 'timeout 20 secs'; end
mixonic 2012年

9

如果使用ZMQ_NOBLOCK,则send不会阻塞,但是如果您尝试关闭套接字和上下文,则此步骤将阻止程序退出。

原因是套接字等待任何对等方,以确保传出消息进入队列。要立即关闭套接字并从缓冲区刷新传出消息,请使用ZMQ_LINGER并将其设置为0。


2
如果不使用zmq.LINGER,则zmq.RCVTIMEO不会为您提供帮助,因为超时后套接字仍不会关闭。这应该添加到所选答案中。
mknaf 2014年

3

如果您只在等待一个套接字,而不是创建一个Poller,则可以执行以下操作:

if work_receiver.poll(1000, zmq.POLLIN):
    print "got message ",work_receiver.recv(zmq.NOBLOCK)
else:
    print "error: message timeout"

如果超时根据情况而变化,则可以使用此方法,而不用设置work_receiver.RCVTIMEO

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.