我有一个应用程序,其中每个websocket连接(在tornado开放回调内)为现有zmq.SUB
设备创建zmq.FORWARDER
套接字。想法是从zmq接收数据作为回调,然后可以通过websocket连接中继到前端客户端。
https://gist.github.com/abhinavsingh/6378134
ws.py
import zmq
from zmq.eventloop import ioloop
from zmq.eventloop.zmqstream import ZMQStream
ioloop.install()
from tornado.websocket import WebSocketHandler
from tornado.web import Application
from tornado.ioloop import IOLoop
ioloop = IOLoop.instance()
class ZMQPubSub(object):
def __init__(self, callback):
self.callback = callback
def connect(self):
self.context = zmq.Context()
self.socket = self.context.socket(zmq.SUB)
self.socket.connect('tcp://127.0.0.1:5560')
self.stream = ZMQStream(self.socket)
self.stream.on_recv(self.callback)
def subscribe(self, channel_id):
self.socket.setsockopt(zmq.SUBSCRIBE, channel_id)
class MyWebSocket(WebSocketHandler):
def open(self):
self.pubsub = ZMQPubSub(self.on_data)
self.pubsub.connect()
self.pubsub.subscribe("session_id")
print 'ws opened'
def on_message(self, message):
print message
def on_close(self):
print 'ws closed'
def on_data(self, data):
print data
def main():
application = Application([(r'/channel', MyWebSocket)])
application.listen(10001)
print 'starting ws on port 10001'
ioloop.start()
if __name__ == '__main__':
main()
forwarder.py
import zmq
def main():
try:
context = zmq.Context(1)
frontend = context.socket(zmq.SUB)
frontend.bind('tcp://*:5559')
frontend.setsockopt(zmq.SUBSCRIBE, '')
backend = context.socket(zmq.PUB)
backend.bind('tcp://*:5560')
print 'starting zmq forwarder'
zmq.device(zmq.FORWARDER, frontend, backend)
except KeyboardInterrupt:
pass
except Exception as e:
logger.exception(e)
finally:
frontend.close()
backend.close()
context.term()
if __name__ == '__main__':
main()
publish.py
import zmq
if __name__ == '__main__':
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.connect('tcp://127.0.0.1:5559')
socket.send('session_id helloworld')
print 'sent data for channel session_id'
但是,我的ZMQPubSub
类似乎根本没有收到任何数据。
我进一步尝试并意识到在ioloop.IOLoop.instance().start()
内注册on_recv
回调后我需要致电ZMQPubSub
。但是,这只会阻止执行。
我也尝试将main.ioloop
实例传递给ZMQStream
构造函数,但也没有帮助。
我是否有办法将ZMQStream
绑定到现有main.ioloop
实例而不会阻止MyWebSocket.open
内的流量?
答案 0 :(得分:5)
在您现在完整的示例中,只需将转发器中的frontend
更改为PULL套接字,将发布者套接字更改为PUSH,它应该按预期运行。
与此相关的套接字选择的一般原则:
最初看起来你可能只想要PUB-SUB,但是一旦你开始查看每个套接字对,你会发现它们非常不同。 frontend-websocket
连接肯定是PUB-SUB - 您可能拥有零到多接收器,并且您只想向消息传递时恰好可用的所有人发送消息。但后端方面则不同 - 只有一个接收器,它绝对需要发布者的每条消息。
所以你有它 - 后端应该是PULL并且前端PUB。所有的插座:
PUSH -> [PULL-PUB] -> SUB
publisher.py:套接字为PUSH
,已连接到device.py中的backend
forwarder.py: backend
为PULL
,frontend
为PUB
ws.py:SUB
连接并订阅forwarder.frontend
。
在您的案例中,PUB / SUB在后端失败的相关行为是慢加入者综合症,即described in The Guide。从本质上讲,订阅者花费有限的时间告诉发布者有关订阅的信息,因此如果您在打开PUB套接字后立即发送消息,可能性尚未被告知它还有任何订阅者,所以它只是丢弃消息。 / p>
答案 1 :(得分:1)
ZeroMq订阅者必须订阅他们希望收到的消息;我在你的代码中没有看到。我相信Python的方式是这样的:
self.socket.setsockopt(zmq.SUBSCRIBE, "")