如何使用pyzmq正确发布和订阅最新消息?

时间:2019-05-17 19:17:03

标签: python zeromq python-multiprocessing pyzmq interprocess

我有一个进程A,该进程不断发布消息,并且进程B和C订阅该主题,并获取发布者在进程A中发布的最新消息。

因此,我将zmq.CONFLATE设置为发布者和订阅者。但是,我发现一个订户无法接收消息。

def publisher(sleep_time=1.0, port="5556"):

    context = zmq.Context()
    socket = context.socket(zmq.PUB)
    socket.setsockopt(zmq.CONFLATE, 1)
    socket.bind("tcp://*:%s" % port)
    print ("Running publisher on port: ", port)

    while True:
        localtime = time.asctime( time.localtime(time.time()))
        string = "Message published time: {}".format(localtime)
        socket.send_string("{}".format(string))
        time.sleep(sleep_time)

def subscriber(name="sub", sleep_time=1, ports="5556"):

    print ("Subscriber Name: {}, Sleep Time: {}, Port: {}".format(name, sleep_time, ports))

    context = zmq.Context()
    print ("Connecting to publisher with ports %s" % ports)
    socket = context.socket(zmq.SUB)
    socket.setsockopt(zmq.CONFLATE, 1)
    socket.setsockopt_string(zmq.SUBSCRIBE, "")
    socket.connect ("tcp://localhost:%s" % ports)

    while True:

        message = socket.recv()
        localtime = time.asctime( time.localtime(time.time()))
        print ("\nSubscriber [{}]\n[RECV]: {} at [TIME]: {}".format(name, message, localtime))
        time.sleep(sleep_time)


if __name__ == "__main__":
    Process(target=publisher).start()
    Process(target=subscriber, args=("SUB1", 1.2, )).start()
    Process(target=subscriber, args=("SUB2", 1.1, )).start()

我试图在发布者中取消设置socket.setsockopt(zmq.CONFLATE, 1),这似乎解决了问题。进程B和C中的两个订户都可以接收消息,并且该消息似乎是最新消息。

我试图找出为什么用CONFLATE设置发布者会导致我遇到的问题。我找不到有关它的信息。有人知道导致这种现象的原因吗?

我还想知道,在一个发布者到多个订阅者的情况下,什么是正确的代码设置,以便订阅者始终可以获取最新消息?

2 个答案:

答案 0 :(得分:1)

最有可能是时间问题,ZMQ_CONFLATE套接字选项将入站和出站队列限制为1条消息。

PUB / SUB的工作方式是,当您设置ZMQ_SUBSCRIBE选项时,订阅者将订阅消息发送给发布者。如果您同时启动两个订阅者,则到达发布者队列的订阅消息之一可能会被丢弃。

尝试在开始的每个订户之间添加睡眠。

来自zeromq文档

  

如果设置,套接字应仅在入站/出站保留一条消息   队列,此消息是收到的最后一条消息/最后一条消息   将被寄出。忽略ZMQ_RCVHWM和ZMQ_SNDHWM选项。才不是   支持多部分消息,特别是其中只有一部分是   保留在套接字内部队列中。

我并不是说这是解决您问题的方法,但是如果是这种情况,我们可能需要将更改发布到libzmq,以使合并选项更加精细,以便您选择将合并应用于入站还是出站队列。

答案 1 :(得分:0)

有一种方法可以在ZMQ订阅套接字中使用“ 仅最新消息”选项(使用CONFLATE选项)。

您需要在订户端。

这里是一个例子:

import zmq

port = "5556"
context = zmq.Context()
socket = context.socket(zmq.SUB)

socket.setsockopt(zmq.SUBSCRIBE, '')
socket.setsockopt(zmq.CONFLATE, 1)  # last msg only.
socket.connect("tcp://localhost:%s" % port)  # must be placed after above options.

while True:
    data = socket.recv()
    print data

换句话说,我删除了订户代码中的所有缓冲队列。


[其他]:

使用zmq.SNDBUFzmq.RCVBUF选项,我们可以设置ZMQ缓冲区大小的限制。 (More complete and an example