zmq in depth
深度解析ZeroMQ PUB/SUB模式,以Python语音描述
PUB操作流程
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import zmq
## 1. create zmq socket
context = zmq.Context()
socket = context.socket(zmq.PUB)
## 2. bind @ IP:PORT
#socket.bind("tcp://*:5556")
socket.bind("tcp://192.168.70.25:5556")
## 3. sending data @ topic ...
topic = "OTA"
while True:
socket.send_string("%s %s" % (topic, "PUB xxx"))
注意:
- 关于bind的IP地址和端口
- 这里应该是本机的IP地址(SUB端与PUB端的IP必须严格一致,参见后续章节)
- 这里的端口号应该是随意指定(需大于1024且避开已占用的端口号)
- 关于topic
- 很隐晦,
send_string()
的第一个参数指定了topic - topic的类型貌似很灵活,可以是一个字符串,也可以是一个数字
- 很隐晦,
SUB操作流程
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import zmq
## 1. create zmq socket
context = zmq.Context()
socket = context.socket(zmq.SUB)
## 2. connect @ IP:PORT
#socket.connect("tcp://localhost:5556")
socket.connect("tcp://192.168.70.25:5556")
## 3. sub @ topic
topic = "OTA" ## "OT" or "O" are also okay
if isinstance(topic, bytes):
topic = topic.decode('ascii')
socket.setsockopt_string(zmq.SUBSCRIBE, topic)
## 4. receiving data ...
for idx in range(10):
string = socket.recv_string()
print(string)
print("---------------------------")
注意:
- 关于connect的IP地址和端口号
- 这里的IP地址与端口号要与PUB端保持一致
- 关于topic
- topic必须按ascii格式编码
- 与PUB端不同,这里要明确指定topic
- topic并非是字符串全匹配
补充:关于IP和PORT
Demo示例代码如下:
# PUB
zmq_socket.bind("tcp://*:5556")
# SUB
zmq_socket.connect("tcp://localhost:5556")
此示例代码中,
- PUB端向其所在机器的所有IP地址发送数据
- SUB端只接收本机(localhost)上PUB端的消息
- 综上,此示例代码只适用本地通信,即SUB端和PUB端都运行在同一台机器上,SUB端才能收到PUB端的消息
为了适用远程(即SUB端和PUB端不在同一台机器),双方必须使用统一的IP地址和PORT端口。根据0MQ的要求,PUB端必须使用本机上所存在的IP地址,而SUB端要connect
到PUB端的地址上。总结来说,PUB端的IP地址就决定了双方通信的唯一IP地址。
举例来说,PUB端与SUB端不在同一台机器上,PUB端的IP为192.168.70.25,而SUB端的IP为192.168.70.20,双方要通信,SUB端必须在connect
时指定PUB端的IP,参考源码如下:
# PUB @ 192.168.70.25
zmq_socket.bind("tcp://192.168.70.25:5556")
# SUB @ 192.168.70.20
zmq_socket.connect("tcp://192.168.70.25:5556")
注意,此种情况下,该SUB端:
- 只能接收到指定IP地址上的PUB端发来的消息
- 接收不到其他IP地址(包括SUB端本机)上的PUB端发的消息
个人的粗浅认识:
按ZeroMQ的设计思想,是没有broker的概念,但在实际编程中,也能明显感觉到其实际上也暗含着server/client的区分,比如PUB端要zmq_socket.bind()
,其可以认为是server,而SUB端要zmq_socket.connect()
,其可以被认为是client,不知道这样理解是否正确。
补充:关于TOPIC
关于topic,这里想着重强调的就是其匹配模式并非是全匹配,而是子串匹配。
还是举例,SUB端订阅了“OTA”这个topic的消息,如果PUB端发出如下topic的消息,SUB端都会收到:
- PUB端在“OTA”这个topic上发出消息,SUB端会收到;
- PUB端在“OTAa”这个topic上发出消息,SUB端会收到;
- PUB端在“OTA_a”这个topic上发出消息,SUB端也会收到;
也就是说,只要PUB端消息的topic开头部分能匹配SUB端订阅的topic,SUB端就能收到此消息;即使PUB端topic与SUB端订阅的topic并不完全一致,SUB端也能收到。
在定义topic时,一定要注意这个情况。