ThinkPHP后台不能直接触发WebSocket推送,必须通过RabbitMQ异步解耦;因PHP-FPM无状态、短生命周期,无法持有WebSocket连接,跨进程push会失败;RabbitMQ仅作可靠消息中转,由独立常驻进程消费并调用Swoole Server推送。

ThinkPHP 后台不能直接通过 HTTP 请求触发 WebSocket 推送,必须引入消息中间件解耦;RabbitMQ 在这里不是替代 WebSocket 的角色,而是承担“缓冲 + 异步分发”的职责——业务层(ThinkPHP)只管发消息到队列,WebSocket 服务端独立消费并推送给在线客户端。
为什么不能让 ThinkPHP 直连 WebSocket 连接池?
PHP-FPM 是无状态、短生命周期的模型,每次请求结束后所有连接(包括 $fd、WebSocket\Server 实例)都会销毁。你无法在控制器里调用 $server->push($fd, $data),因为 $server 根本不存在。强行用 Redis 存 fd 并尝试跨进程 push,会因 Swoole Worker 进程隔离导致推送失败或段错误。
- ThinkPHP 运行在 PHP-FPM 模式下,和 WebSocket 服务是两个完全分离的进程
- 即使你用
think-swoole启动了 WebSocket 服务,它的Server实例也只存在于 Swoole 主进程及其 Worker 中,PHP-FPM 完全不可见 - 试图用
cURL调用本地 HTTP API 再由该 API 调用$server->push(),必须确保该 API 是由同一个 Swoole 进程提供(比如用think-swoole的 HTTP 服务),否则仍是跨进程无效操作
RabbitMQ 在链路中到底干啥?
RabbitMQ 不处理 WebSocket 握手、不管理连接、不解析帧数据,它只做一件事:把 ThinkPHP 发出的业务事件(如“用户 A 给 B 发了条消息”)可靠地转交给 WebSocket 服务进程。它解决的是“异步解耦”和“削峰”,不是“协议转换”。
- ThinkPHP 控制器中执行
$channel->basic_publish(),把消息丢进chat.notify队列,立刻返回,不等推送结果 - WebSocket 服务(独立运行的 Swoole 或 Workerman 进程)作为消费者,长期订阅该队列,收到后查 Redis 获取在线用户
fd映射表,再调用$server->push() - 若用户离线,消息可暂存在 Redis
user:1001:offline列表中,上线时拉取——这个逻辑必须写在 WebSocket 消费端,不能放在 ThinkPHP 里 - 别用 RabbitMQ 的
web-stomp插件直接暴露给浏览器,那会绕过你的身份校验和业务逻辑,属于架构倒置
WebSocket 服务端必须自己写消费者,不能依赖 think-swoole 自带路由
think-swoole 的 websocket.php 路由只响应客户端主动发来的消息帧(如 {"type":"chat.send", "data":...}),对后端业务触发的“推送”无感知。你要额外启动一个常驻进程来监听 RabbitMQ,这个进程和 php think swoole 启动的 WebSocket Server 可以是同一个 Swoole 实例(推荐),也可以是分开的 Workerman 进程(更易调试)。
立即学习“PHP免费学习笔记(深入)”;
- 在
app/command/ConsumeRabbitMQ.php中定义命令,使用php-amqplib/php-amqplib连接 RabbitMQ - 在
handle()方法里启动一个死循环:$channel->basic_consume(...),并在回调中调用$this->broadcastToUser($userId, $data) - 广播方法需从 Redis 读
user_fd_map,遍历$server->exist($fd)确认连接有效后再push,避免invalid fd错误 - 不要在消费者里 new 一个新
Swoole\WebSocket\Server,必须复用已启动的实例——可通过ServerManager::get()(think-swoole 提供)或全局变量注入获取
最容易被忽略的三个细节
一是心跳必须由 WebSocket 服务端主动发(ping 帧),不能只靠客户端定时 send;二是 RabbitMQ 消费者要手动 basic_ack,否则消息会不断重投;三是 Redis 中存储的 fd 必须带服务器 ID 前缀(如 s1:fd:123),多机部署时避免不同节点的 fd 冲突。



















