req/rep模式不能直接用于生产微服务,因其严格轮转机制(客户端须发→收→发→收,服务端须收→发→收→发)导致错序或跳步即永久阻塞,且无内置重试、超时、断线重连与负载均衡能力。

PyZMQ 能直接支撑微服务间高性能通信,不需要额外部署消息中间件,但必须避开几个关键陷阱才能稳定运行。
REQ/REP 模式为什么不能直接用于生产微服务?
看似最像 HTTP 的 REQ/REP 模式,实际在微服务中容易卡死:客户端发完请求后必须等响应,服务端收完请求后必须立刻回,任何一方挂掉或超时,另一方就永久阻塞。
-
REQsocket 会自动排队最多一个未完成请求,再发就阻塞;REP必须严格按“recv → send”顺序调用,错一次整个 socket 就失效 - 没有内置重试、超时、断线重连——得自己封装
socket.setsockopt(zmq.RCVTIMEO, 5000)和socket.setsockopt(zmq.SNDTIMEO, 5000) - 不支持负载均衡:多个
REP实例无法被单个REQ自动分发,得换DEALER/ROUTER或加代理
PUB/SUB 模式如何避免消息丢失?
PUB 发送不关心谁在听,SUB 订阅后才开始收——这意味着启动顺序错乱时,SUB 会漏掉 PUB 在它连接前发出的消息。
- 必须在
sub_socket.setsockopt_string(zmq.SUBSCRIBE, "")后,再调用sub_socket.connect(...),否则订阅无效 - 若需可靠投递,不能只靠
PUB/SUB,要搭配XPUB/XSUB代理或改用PUSH/PULL+ 确认机制 -
SUB默认接收所有主题,但空订阅字符串""仍需显式设置,否则收不到任何消息
跨服务通信时,TCP 地址绑定和连接要注意什么?
本地测试用 "tcp://*:5555" 和 "tcp://localhost:5555" 没问题,但容器或跨主机部署时,* 绑定的是所有网卡,localhost 却只走回环,两者根本不通。
- 服务端应绑定
"tcp://0.0.0.0:5555"(不是*,某些旧版 libzmq 不识别*) - 客户端连接地址必须是服务端实际可访问的 IP,比如 Docker 内部网络用
"tcp://service_name:5555",K8s 用 headless service DNS - 防火墙、SELinux、容器 network mode(如
hostvsbridge)都会影响 TCP 连通性,先用telnet ip port验证底层通路
如何让 PyZMQ 与 asyncio 兼容而不阻塞事件循环?
socket.recv() 和 socket.send() 默认是同步阻塞的,直接丢进 async def 里会拖垮整个协程调度。
- 用
socket.setblocking(False)+asyncio.wait_for()做非阻塞轮询,但效率低 - 推荐方案:用
zmq.asyncio.Context()替代zmq.Context(),然后直接 awaitsocket.recv()和socket.send() - 注意:
zmq.asyncio从 PyZMQ 22.0+ 才稳定支持,旧版本需降级或换zmq.Poller+asyncio.to_thread
zmq.Context() 应全局唯一,每个服务进程只创建一次;而 socket 对象不能跨线程共享,也不能在 fork 后复用——这些细节不处理,轻则内存泄漏,重则静默丢消息。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











