PHP用ZeroMQ实现可靠通信需选对socket类型、配对模式并管理好上下文生命周期:REQ/REP用于严格请求-应答,PUSH/PULL用于单向负载均衡任务分发,PUB/SUB用于发布订阅(SUB需显式订阅且PUB须先bind);服务端bind、客户端connect;ZMQContext应全局复用,socket需显式close和disconnect;消息收发需处理JSON编码失败、多帧接收及超时设置。

PHP 要用 ZeroMQ 实现可靠通信,核心不是“能不能连上”,而是选对 socket 类型、配对模式、以及处理好上下文生命周期。直接上 zmq_send() 却收不到消息?八成是模式没对齐,或 bind()/connect() 方向反了。
怎么选 socket 类型和通信模式
ZeroMQ 不是传统队列服务,没有中心 broker;它靠 socket 类型 + 模式组合决定行为。PHP 中最常用三组:
-
ZMQ::SOCKET_REQ和ZMQ::SOCKET_REP:请求-应答,必须严格一问一答,REQ发完必须等REP回,否则阻塞;适合 RPC 场景,但不支持广播或多客户端并发请求 -
ZMQ::SOCKET_PUSH和ZMQ::SOCKET_PULL:单向管道,自动负载均衡;PUSH发给所有已连接的PULL,轮询分发;适合任务分发(如异步 Worker),但无反馈机制 -
ZMQ::SOCKET_PUB和ZMQ::SOCKET_SUB:发布订阅;SUB必须先调用setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "")才能收到所有消息,空字符串表示订阅全部;注意 PUB 启动前若无 SUB 连接,消息会丢失(ZeroMQ 默认不缓存)
为什么 bind() 和 connect() 顺序不能乱
ZeroMQ 的 bind() 和 connect() 不是“谁主动谁 client”,而是由角色决定:服务端(长期运行、接受连接)用 bind(),客户端(启动快、临时连接)用 connect()。常见错误:
- 把
PUB写成connect("tcp://127.0.0.1:5555")—— 它该bind(),否则订阅者连不上 - 多个
PULL都bind()等待PUSH,结果PUSH只能连一个,其余闲置 -
REQ先connect(),但REP还没bind()完就发请求 → 报错ZMQ_EAGAIN或静默失败
PHP-ZMQ 扩展安装和上下文管理要点
扩展必须用 pecl install zmq(非 zmq-beta,后者已废弃),PHP 8+ 用户确认编译时启用了 --with-zmq。关键陷阱在资源释放:
立即学习“PHP免费学习笔记(深入)”;
- 每个
ZMQContext应全局复用,不要每次通信都new ZMQContext();频繁创建销毁会导致文件描述符泄漏 -
ZMQSocket关闭后,必须显式调用$socket->disconnect()(如果连过)和$socket->close(),否则上下文无法彻底释放 - 脚本结束前不调用
$context->destroy(),可能引发 segfault(尤其 CLI 模式下)
消息收发要注意编码和边界
ZeroMQ 默认按二进制传输,PHP 中字符串不含隐式编码转换:
- 发送 JSON 数据,别直接
$socket->send(json_encode($data)),要确保json_encode()不返回false,否则发的是空字符串 - 接收多帧消息(如
PUB/SUB带 topic),得用循环recvMulti()或检查getSockOpt(ZMQ::SOCKOPT_RCVMORE),否则只取到第一帧 -
send()默认阻塞,高并发下建议设超时:$socket->setSockOpt(ZMQ::SOCKOPT_SNDTIMEO, 1000),避免卡死整个进程
最容易被忽略的是模式语义——比如想用 PUB/SUB 做双向通信,硬加 REQ/REP 在同一端口,结果发现消息全乱序或丢帧。ZeroMQ 的力量来自组合,而不是叠加;一个逻辑通道只做一件事,才稳。



















