【发布时间】:2015-09-01 01:39:07
【问题描述】:
我实现了 ZMQ (http://zguide.zeromq.org/php:chapter5#Last-Value-Caching) 的最后一个值缓存 (LVC) 示例,但无法让第二个订阅者在后端注册。
订阅者第一次加入时,满足event[0] == b'\x01' 条件并发送缓存值,但第二个订阅者(相同主题)甚至没有注册(if backend in events: 永远不会为真)。其他一切正常。数据从发布者传递给订阅者(全部)。
这可能是什么原因?后端连接方式是否正确?这种模式是否只适用于第一个订阅者?
更新
当我为第二个订阅者订阅另一个主题时,我得到了正确的行为(即订阅时\x01)。这似乎真的适用于第一个订阅者 onlt 。 ZeroMQ 中是否存在错误?
更新 2
这是一个最小的工作示例,显示 LVC 模式不工作(至少不是这里的实现方式)。
# subscriber.py
import zmq
def main():
ctx = zmq.Context.instance()
sub = ctx.socket(zmq.SUB)
sub.connect("tcp://127.0.0.1:5558")
# Subscribe to every single topic from publisher
print 'subscribing (sub side)'
sub.setsockopt(zmq.SUBSCRIBE, b"my-topic")
poller = zmq.Poller()
poller.register(sub, zmq.POLLIN)
while True:
try:
events = dict(poller.poll(1000))
except KeyboardInterrupt:
print("interrupted")
break
# Any new topic data we cache and then forward
if sub in events:
msg = sub.recv_multipart()
topic, current = msg
print 'received %s on topic %s' % (current, topic)
if __name__ == '__main__':
main()
这里是代理(如示例中所示,但更冗长且集成了发布者)。
# 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()
运行此代码 (1 x broker.py, 2 x subscriber.py) 表明第一个订阅者按预期在代理注册(\x01 和缓存查找),但第二个订阅者没有以相同的方式注册.有趣的是,第二个订阅者连接到 pub/sub 频道,一段时间(10 秒)后,两个订阅者都收到了来自发布者的数据。
这很奇怪。也许我的一些图书馆已经过时了。这是我得到的:
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 和 libsodium)。
更新 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
【问题讨论】:
标签: python sockets proxy zeromq publish-subscribe