ZeroMQ结合Python使用时循环无sleep命令无法发送消息问题咨询
ZeroMQ循环无sleep发送失败原因
核心原因1:PUB-SUB模式的慢连接问题
ZMQ的PUB-SUB模式存在经典的*慢连接(Slow Joiner)*问题:PUB端绑定端口后,SUB端的连接需要完成TCP三次握手、ZMQ内部会话初始化、订阅规则同步等流程,全程需要几毫秒到几十毫秒的耗时。
无sleep的发布端代码中,100次发送循环会在几毫秒内全部执行完成,此时SUB端大概率还未完成连接建立,而PUB模式的默认策略是无有效订阅者连接时直接丢弃所有消息,因此订阅端无法收到任何数据。添加0.1s的sleep后,给了连接建立预留了充足时间,后续消息就能正常投递到已连接的SUB端。
核心原因2:异步发送与进程提前退出
ZMQ的send方法是异步接口,调用后数据不会立刻发送到网络,而是先写入ZMQ内部的发送队列,由后台IO线程负责实际网络传输。无sleep的代码循环执行完后,Python进程会直接退出,此时IO线程可能还未处理完队列中的消息,资源就被系统回收,也会导致数据丢失。
正确解决方案
- 避免依赖sleep这种不可靠的临时方案,改用端到端同步机制:新增一条REQ/REP或者PUSH/PULL的同步通道,SUB端完成连接和订阅配置后,先通过同步通道给PUB端发送就绪信号,PUB端收到信号后再开始发送业务数据,从根源上避免慢连接丢包。
- 配置LINGER参数:创建socket后添加
socket.setsockopt(zmq.LINGER, 1000),表示关闭socket时最多等待1000毫秒让队列中的消息发送完成,避免进程提前退出导致消息丢失。 - 测试场景可简化处理:仅在PUB端bind完成后添加一次
time.sleep(0.2)即可,不需要在循环内每次发送都加sleep,也能解决问题。
相关复现代码
可正常运行的发布端代码(zero_mq_pub.py)
import signal import time import zmq import random signal.signal(signal.SIGINT, signal.SIG_DFL) context = zmq.Context() socket = context.socket(zmq.PUB) socket.bind('tcp://*:5555') float_no = str(random.uniform(20,60)).encode() for i in range(100): float_no = str(random.uniform(20,60)).encode() socket.send(float_no) time.sleep(0.1)
无法正常运行的发布端代码(zero_mq_pub.py)
import signal import time import zmq import random signal.signal(signal.SIGINT, signal.SIG_DFL) context = zmq.Context() socket = context.socket(zmq.PUB) socket.bind('tcp://*:5555') float_no = str(random.uniform(20,60)).encode() for i in range(100): float_no = str(random.uniform(20,60)).encode() socket.send(float_no)
订阅端代码(zero_mq_receiver.py)
import signal import zmq signal.signal(signal.SIGINT, signal.SIG_DFL) context = zmq.Context() socket = context.socket(zmq.SUB) socket.connect('tcp://localhost:5555') socket.setsockopt(zmq.SUBSCRIBE, b'') while True: message = socket.recv_string() float_message = float(message) print("type is: ", type(float_message), " the message is ", float_message )
内容的提问来源于stack exchange,提问作者resolver101
相关产品推荐
相关产品推荐

