ZeroMQ中能否获取发布者与订阅者的身份及IP地址?
ZeroMQ PUB-SUB模式下获取对方IP的解决方案
好问题!我刚好在ZeroMQ相关的项目里处理过类似的需求,咱们把这两个问题拆开来一步步说清楚:
发布者能否获取所有订阅者的IP?
首先明确:ZeroMQ原生的PUB-SUB模式里,发布者默认是拿不到订阅者的IP地址的。因为ZeroMQ的设计理念是封装底层网络细节,PUB套接字只负责发送消息,不会主动追踪连接过来的订阅者信息。
但要实现这个需求也不难,咱们可以通过「订阅者主动上报」的方式来搞定:
- 订阅者在完成主题订阅后,通过一个额外的通信通道(比如单独的PUSH-PULL套接字对)向发布者发送自己的IP地址
- 发布者维护一个集合来存储所有上报的订阅者IP,这样就能实现追踪和展示的效果
举个简单的Python伪代码示例,方便你理解:
发布者端代码片段
import zmq import time context = zmq.Context() # 用于发布业务消息的PUB套接字 pub_socket = context.socket(zmq.PUB) pub_socket.bind("tcp://*:5555") # 用于接收订阅者IP上报的PULL套接字 pull_socket = context.socket(zmq.PULL) pull_socket.bind("tcp://*:5556") # 存储已注册的订阅者IP subscribers = set() while True: # 非阻塞接收订阅者的IP上报 try: sub_ip = pull_socket.recv_string(flags=zmq.NOBLOCK) if sub_ip not in subscribers: subscribers.add(sub_ip) print(f"新增订阅者(主题'foo'): {sub_ip}") except zmq.Again: pass # 正常发布业务消息 pub_socket.send_string("foo: 这是一条测试消息") time.sleep(1)
订阅者端代码片段
import zmq import socket context = zmq.Context() # 订阅业务消息的SUB套接字 sub_socket = context.socket(zmq.SUB) sub_socket.connect("tcp://192.168.1.1:5555") sub_socket.setsockopt_string(zmq.SUBSCRIBE, "foo") # 获取本地对外的IP地址(适配不同网络场景) def get_local_public_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # 连接公共DNS服务器来获取当前出口IP s.connect(("8.8.8.8", 80)) local_ip = s.getsockname()[0] finally: s.close() return local_ip # 向发布者上报自身IP的PUSH套接字 push_socket = context.socket(zmq.PUSH) push_socket.connect("tcp://192.168.1.1:5556") push_socket.send_string(get_local_public_ip()) # 接收发布者的业务消息 while True: msg = sub_socket.recv_string() print(f"收到消息: {msg}")
订阅者能否获取发布者的IP?
这个就简单多了!订阅者完全可以获取发布者的IP地址,有两种常用方式:
- 直接使用你在
connect方法里硬编码的发布者地址(毕竟是你主动连过去的,肯定知道目标IP) - 通过ZeroMQ的套接字选项,动态获取实际连接的对等方地址(适合动态配置发布者地址的场景)
比如用第二种方式的Python代码示例:
import zmq context = zmq.Context() sub_socket = context.socket(zmq.SUB) sub_socket.connect("tcp://192.168.1.1:5555") sub_socket.setsockopt_string(zmq.SUBSCRIBE, "foo") # 获取最后一次连接的发布者端点信息 peer_endpoint = sub_socket.getsockopt_string(zmq.LAST_ENDPOINT) # 从端点字符串中提取IP地址 pub_ip = peer_endpoint.split("://")[1].split(":")[0] print(f"当前连接的发布者(主题'foo'): {pub_ip}")
需要注意的是,如果订阅者同时连接了多个发布者,LAST_ENDPOINT只会返回最后一次连接的地址,这种情况下你需要自己维护每个发布者的连接信息。
内容的提问来源于stack exchange,提问作者Mark Sanchez
相关产品推荐
相关产品推荐

