Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python ZMQ:多个订阅者的XPUB套接字上没有订阅消息(最后一个值缓存模式)_Python_Sockets_Proxy_Zeromq_Publish Subscribe - Fatal编程技术网

Python ZMQ:多个订阅者的XPUB套接字上没有订阅消息(最后一个值缓存模式)

Python ZMQ:多个订阅者的XPUB套接字上没有订阅消息(最后一个值缓存模式),python,sockets,proxy,zeromq,publish-subscribe,Python,Sockets,Proxy,Zeromq,Publish Subscribe,我实现了ZMQ()的最后一个值缓存(LVC)示例,但无法让第二个订户在后端注册 订阅者第一次登船时,满足事件[0]==b'\x01'条件并发送缓存值,但第二个订阅者(同一主题)甚至没有注册(如果事件中的后端:从不为真)。其他一切都很好。数据从发布服务器传递到订阅服务器(全部) 这可能是什么原因?后端的连接方式是否正确?此模式是否仅适用于第一个订阅者 更新 当我向第二个订阅者订阅另一个主题时,我获得了正确的行为(即订阅时的\x01)。这似乎真的适用于第一个用户onlt。ZeroMQ中是否存在bug

我实现了ZMQ()的最后一个值缓存(LVC)示例,但无法让第二个订户在后端注册

订阅者第一次登船时,满足
事件[0]==b'\x01'
条件并发送缓存值,但第二个订阅者(同一主题)甚至没有注册(
如果事件中的后端:
从不为真)。其他一切都很好。数据从发布服务器传递到订阅服务器(全部)

这可能是什么原因?后端的连接方式是否正确?此模式是否仅适用于第一个订阅者

更新

当我向第二个订阅者订阅另一个主题时,我获得了正确的行为(即订阅时的
\x01
)。这似乎真的适用于第一个用户onlt。ZeroMQ中是否存在bug

更新2

下面是一个简单的工作示例,它表明LVC模式不工作(至少不是这里实现的方式)

这里是代理(如示例中所示,但更详细一些,并且有一个集成的发布者)

运行此代码(1 x
broker.py
,2 x
subscriber.py
)表明第一个订阅服务器按预期在代理服务器上注册(
\x01
和缓存查找),但第二个订阅服务器的注册方式不同。有趣的是,第二个订阅者连接到发布/订阅频道,因为在一段时间(10秒)后,两个订阅者都从发布者接收数据

# broker.py
# from http://zguide.zeromq.org/py:lvcache
import zmq
import threading
import time


class Publisher(threading.Thread):
    def __init__(self):
        super(Publisher, self).__init__()

    def run(self):
        time.sleep(10)
        ctx = zmq.Context.instance()
        pub = ctx.socket(zmq.PUB)
        pub.connect("tcp://127.0.0.1:5557")

        cnt = 0
        while True:
            msg = 'hello %d' % cnt
            print 'publisher is publishing %s' % msg
            pub.send_multipart(['my-topic', msg])
            cnt += 1
            time.sleep(5)


def main():
    ctx = zmq.Context.instance()
    frontend = ctx.socket(zmq.SUB)
    frontend.bind("tcp://*:5557")
    backend = ctx.socket(zmq.XPUB)
    backend.bind("tcp://*:5558")

    # Subscribe to every single topic from publisher
    frontend.setsockopt(zmq.SUBSCRIBE, b"")

    # Store last instance of each topic in a cache
    cache = {}

    # We route topic updates from frontend to backend, and
    # we handle subscriptions by sending whatever we cached,
    # if anything:
    poller = zmq.Poller()
    poller.register(frontend, zmq.POLLIN)
    poller.register(backend, zmq.POLLIN)


    # launch a publisher
    p = Publisher()
    p.daemon = True
    p.start()

    while True:

        try:
            events = dict(poller.poll(1000))
        except KeyboardInterrupt:
            print("interrupted")
            break

        # Any new topic data we cache and then forward
        if frontend in events:
            msg = frontend.recv_multipart()
            topic, current = msg
            cache[topic] = current
            backend.send_multipart(msg)

        ### this is where it fails for the 2nd subscriber. 
        ### There's never even an event from the backend 
        ### in events when the 2nd subscriber is subscribing.

        # When we get a new subscription we pull data from the cache:
        if backend in events:
            print 'message from subscriber'
            event = backend.recv()
            # Event is one byte 0=unsub or 1=sub, followed by topic
            if event[0] == b'\x01':
                topic = event[1:]
                print ' => subscribe to %s' % topic
                if topic in cache:
                    print ("Sending cached topic %s" % topic)
                    backend.send_multipart([ topic, cache[topic] ])
            elif event[0] == b'\x00':
                topic = event[1:]
                print ' => unsubscribe from %s' % topic

if __name__ == '__main__':
    main()
这很奇怪。也许我的一些图书馆已经过时了。以下是我得到的:

Python 2.7.9 (v2.7.9:648dcafa7e5f, Dec 10 2014, 10:10:46) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import zmq
>>> zmq.__version__
'14.1.1'

$ brew info zeromq
zeromq: stable 4.0.5 (bottled), HEAD
High-performance, asynchronous messaging library
http://www.zeromq.org/
/usr/local/Cellar/zeromq/4.0.5_2 (64 files, 2.8M) *
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/zeromq.rb
==> Dependencies
Build: pkg-config ✔
Optional: libpgm ✘, libsodium ✘
更新3

zeromq 4.1.2
pyzmq-14.7.0
(安装或不安装libpgm和libnaude)中也可以观察到这种行为

更新4

另一个观察结果表明,对第一个订阅者的处理方式有所不同:第一个订阅者是唯一一个以预期方式从
XPUB
套接字(
backend
)取消订阅的订阅者,在其订阅主题前面加上
\x00
。其他订阅者(我尝试了2个以上)在后端通道上保持沉默(尽管接收消息)

更新5

我希望我没有陷入兔子洞,但是我研究了
czmq
绑定,并用C运行了我的Python示例。结果是一样的,所以我想这不是绑定的问题,而是
libzmq
的问题

我还验证了第二个订户正在发送订阅消息,事实上,我可以在网上看到:

首次订阅:

0000  02 00 00 00 45 00 00 3f  98 be 40 00 40 06 00 00   ....E..? ..@.@...
0010  7f 00 00 01 7f 00 00 01  fa e5 15 b6 34 f0 51 c3   ........ ....4.Q.
0020  05 e4 8b 77 80 18 31 d4  fe 33 00 00 01 01 08 0a   ...w..1. .3......
0030  2a aa d1 d2 2a aa cd e9  00 09 01 6d 79 2d 74 6f   *...*... ...my-to
0040  70 69 63                                           pic              
第二个订阅消息,标记并解释差异(如上)。在订阅帧中发送相同的数据

                               identification
                               v
0000  02 00 00 00 45 00 00 3f  ed be 40 00 40 06 00 00   ....E..? ..@.@...
                             src port      sequence number
                                  v        v  v  v  v
0010  7f 00 00 01 7f 00 00 01  fa e6 15 b6 17 da 02 e7   ........ ........

Acknowledgement number   window scaling factor
      v  v  v  v           v
0020  71 4b 33 e6 80 18 31 d5  fe 33 00 00 01 01 08 0a   qK3...1. .3......

timestamp value  timestamp echo reply
            v           v  v   |<-------- data -------
0030  2a aa f8 2c 2a aa f4 45  00 09 01 6d 79 2d 74 6f   *..,*..E ...my-to

      ------>|
0040  70 69 63                                           pic              
识别
v
0000 02 00 00 45 00 00 3f教育为40 00 40 06 00…E…。@。。。
src端口序列号
v v v v
0010 7f 00 01 7f 00 01 fa e6 15 b6 17 da 02 e7。。。。。。。。
确认数窗口比例因子
v v v v
0020 71 4b 33 e6 80 18 31 d5 fe 33 00 00 01 01 08 0a qK3…1。3.
时间戳值时间戳回显应答
v v v||
0040706963图

我找到了这个问题的解决方案,尽管我前后阅读了文档,但我没有看到它。关键是
XPUB\u VERBOSE
。在后端初始化之后添加这一行,一切都正常

backend.setsockopt(zmq.XPUB_VERBOSE, True)
以下是摘录:

ZMQ\u XPUB\u VERBOSE
:在
XPUB
套接字上提供所有订阅消息 设置新订阅和订阅的
XPUB
套接字行为 退订。值0是默认值,仅传递新值 向上游发送订阅消息。值为1时,将传递所有信息 订阅消息上游

选项值类型int选项值单位0,1默认值0 适用的插座类型
ZMQ\u XPUB

Pieter Hintjens对此有更多信息。这是相关章节:

几个月前,我们添加了一个简洁的小选项(
ZMQ\u XPUB\u VERBOSE
XPUB
sockets,用于禁用对重复订阅的筛选。 这现在适用于任何数量的订阅者。我们的使用方法如下:

void *publisher = zsocket_new (ctx, ZMQ_XPUB);
zsocket_set_xpub_verbose (publisher, 1);
zsocket_bind (publisher, "tcp://*:6001");
应该更新LVC模式描述以反映此设置,否则此模式将无法工作

void *publisher = zsocket_new (ctx, ZMQ_XPUB);
zsocket_set_xpub_verbose (publisher, 1);
zsocket_bind (publisher, "tcp://*:6001");