zeromq pub-sub 必须使用 zmq_pub/zmq_sub 套接字类型,pub 只能 bind()、sub 必须 connect(),sub 需显式设置订阅过滤器(如 "" 订阅全部),消息无缓存易丢失,需同步机制或 ack 确认;c++ 中需注意 zmq_msg_t 生命周期管理及多线程安全。

Pub-Sub 模式必须用 ZMQ_PUB / ZMQ_SUB 套接字类型
ZeroMQ 的 Pub-Sub 不是“自动发现”或“消息广播”,而是严格依赖套接字类型和显式连接方向:ZMQ_PUB 只能 bind()(不能 connect()),ZMQ_SUB 必须 connect() 到 PUB 端地址。反着来会静默失败——比如 SUB bind() 后无任何错误,但永远收不到消息。
常见错误现象:zmq_msg_recv() 长时间阻塞、返回 0 字节、或直接超时;检查 zmq_socket_type 和调用顺序即可定位。
- PUB 端启动后必须先
bind(),再发送;SUB 端必须等 PUB 已 bind 后才connect() - SUB 默认过滤所有消息,需至少调用一次
setsockopt(ZMQ_SUBSCRIBE, ...),空字符串""表示订阅全部 - 不要在同一个线程里混用多个 ZMQ_PUB 套接字去 bind 同一端口——会报
Address already in use
消息丢失问题:PUB 启动前 SUB 已连接?
ZeroMQ 的 Pub-Sub 是“无状态”的,PUB 套接字不缓存历史消息。如果 SUB 先 connect()、PUB 后 bind() 并立刻发消息,这部分消息大概率丢失——这是设计使然,不是 bug。
解决思路不是“等连接就绪”,而是用同步机制(如 REQ-REP 握手)或延时发送。更稳妥的做法是让 PUB 启动后主动等待已知 SUB 连接数(通过 ZMQ_EVENTS 或外部协调),但 ZeroMQ 本身不暴露连接数。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 简单场景下,在 PUB 发送前加
std::this_thread::sleep_for(100ms)可缓解(仅测试用) - 生产环境应引入“订阅确认”逻辑:SUB 收到首个消息后发回 ACK,PUB 收到才开始业务发布
- 避免用
inproc://测试时忽略该问题——inproc 通道建立极快,容易掩盖丢失现象
如何设置 SUB 订阅过滤器
ZMQ_SUB 的过滤发生在内核/ZeroMQ 内部,只对消息的前 N 字节做字面匹配。过滤器是二进制字符串,不是正则;且一个 SUB 套接字可设置多个过滤器(多次调用 setsockopt(ZMQ_SUBSCRIBE, ...))。
例如想订阅以 "stock." 开头的消息,需传入 "stock."(注意无 null 终止符);若传入 "stock\0",则只会匹配两个字节 + \0,几乎不命中。
- 订阅全部消息:用空字符串
"",即sub_sock.setsockopt(ZMQ_SUBSCRIBE, "", 0) - 取消订阅:调用
setsockopt(ZMQ_UNSUBSCRIBE, key, key_len);没有“清空所有”的 API,需记录并逐个取消 - 过滤器长度最大为 256 字节;超过部分被截断,不会报错
C++ 中需手动管理 zmq_msg_t 生命周期
使用 zmq_msg_init() / zmq_msg_init_size() 创建消息体后,必须配对调用 zmq_msg_close(),否则内存泄漏。RAII 封装虽好,但官方 C++ binding(libzmq 自带)不提供,得自己写或用第三方(如 cppzmq)。
cppzmq 更安全:它把 zmq::message_t 设计为 RAII 类型,析构自动 close;发送时直接传引用,无需手动 manage。
- 裸 C API 示例中,
zmq_msg_send(&msg, sock, 0)成功后仍需zmq_msg_close(&msg) - 若用
zmq_msg_init_data()指向栈内存,务必确保数据生命周期长于 send 调用——PUB 端 send 返回不等于消息已送达 - 多线程中不要共享同一
zmq_msg_t实例;每个线程应有自己的消息对象
ZMQ_SNDHWM / ZMQ_RCVHWM 和系统 net.core.wmem_max 才能稳住吞吐。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










