为什么 Zyre 演员的 PAIR 插座使用 LINGER=0 和 SNDTIMEO=0?

Why are LINGER=0 and SNDTIMEO=0 used for Zyre actors' PAIR sockets?

查看 Pyre 的(Python 版本的 Zyre)源代码,我看到了以下内容:

def zcreate_pipe(ctx, hwm=1000):
    backend = zsocket.ZSocket(ctx, zmq.PAIR)
    frontend = zsocket.ZSocket(ctx, zmq.PAIR)
    # ...
    # close immediately on shutdown
    backend.setsockopt(zmq.LINGER, 0)
    frontend.setsockopt(zmq.LINGER, 0)

class ZActor(object):
    # ...

    def __init__(self, ctx, actor, *args, **kwargs):
        # ...
        self.pipe, self.shim_pipe = zhelper.zcreate_pipe(ctx)
        # ...

    def run(self):
        self.shim_handler(*self.shim_args, **self.shim_kwargs)
        self.shim_pipe.set(zmq.SNDTIMEO, 0)
        self.shim_pipe.signal()
        self.shim_pipe.close()

    def destroy(self):
        # ...
        self.pipe.set(zmq.SNDTIMEO, 0)
        self.pipe.send_unicode("$TERM")
        self.pipe.wait()
        self.pipe.close()

我感兴趣的是 LINGER=0SNDTIMEO=0 的用法。

对应的文档是here and here:

ZMQ_SNDTIMEO: Maximum time before a send operation returns with EAGAIN

[rather self-explanatory]

ZMQ_LINGER: Set linger period for socket shutdown

[...] The linger period determines how long pending messages which have yet to be sent to a peer shall linger in memory after a socket is closed with zmq_close(3), and further affects the termination of the socket's context with zmq_term(3). [...]

  • [...]

  • The value of 0 specifies no linger period. Pending messages shall be discarded immediately when the socket is closed with zmq_close().

  • [...]

所以简而言之,两个方向的最后一条消息可能都没有发送。如果 send 会阻塞,SNDTIMEO=0 会启动,并且(假设发送队列中仍有内容)LINGER=0 可能会在 close.[=23 期间丢弃消息=]

这似乎是个坏主意,因为如果 $TERM 被丢弃,actor 不会被杀死,而如果 signal 被丢弃,调用线程只会阻塞。对我来说唯一有意义的方法是如果消息可能永远不会被丢弃(因为 PAIR over inproc:// 传输的某些特性?),但是,为什么首先使用套接字选项?

是什么让这段代码按预期工作,为什么要这样使用套接字选项,以及在什么情况下should/shouldn我不遵循这个例子?

对我来说这看起来像是一个潜在的错误 (deadlock/hang)。如果 actor 没有足够快地读取发送给它的消息,队列(大小 'hwm')可能已满 - 这意味着 zmq 不会发送任何东西,并且 destroy() 将最终等待信号actor 退出时预期 - 但由于 actor 从未收到“$TERM”,它无法做出反应 - 除非其 recv() 中存在超时,否则它也可能永远等待消息。

[我注意到在 destroy() 方法中的 wait() 之前似乎有一条持怀疑态度的评论 - 所以您可能不是第一个注意到这一点的人]

我会小心处理 destroy() - 实际上,您可以编写您的解决方案,以便极不可能溢出队列 - 或者您可以检查发送是否在 destroy() 中成功 - 如果没有 - 或者再试一次(超时)或跳过 wait()。