WebSocket断开主因是Swoole心跳配置不匹配,需调大heartbeat_idle_time并配heartbeat_check_interval;客户端须主动发ping;fd跨进程无效,应存Redis/Swoole\Table并带worker_id;onOpen禁用同步IO;Nginx需透传Upgrade头。

WebSocket 连接断开后不重连?检查 swoole_websocket_server 的心跳与超时配置
ThinkPHP 自身不内置 WebSocket 服务,所谓“集成”实际是依赖 Swoole 扩展启动独立长连接服务,TP 只负责消息路由和业务逻辑。很多项目卡在连接建立后几秒自动断开,根本原因是 swoole_websocket_server 默认的 heartbeat_idle_time(默认 60 秒)和客户端 ping/pong 不匹配。
- 客户端必须主动发
ping,服务端才能触发onMessage中的pong响应;若客户端静默,Swoole 会在 idle 超时后强制 close 连接 - 在
server.php启动脚本中显式设置:'heartbeat_idle_time' => 300(5 分钟),同时配'heartbeat_check_interval' => 10,避免误杀活跃连接 - 浏览器 WebSocket 实例不会自动发 ping,得自己用
setInterval(() => ws.send(''), 5000)维持心跳——别依赖 onopen 后就万事大吉
定时任务推送消息到指定 WebSocket 连接?别直接用 think\facade\Cache 存 fd
Swoole 的 fd(文件描述符)只在当前 Worker 进程内有效,跨进程或重启后失效。用 TP 的缓存驱动(如 file、redis)存 fd 看似可行,但极易导致“推送无声”:定时任务跑在另一个进程里,server->push($fd, $msg) 会返回 false 且无报错。
- 正确做法是用 Swoole 提供的
Server->getClientInfo($fd)验证 fd 是否存活,或改用Server->exist($fd)判断 - 更稳妥的是用「用户 ID → fd 映射表」存在 Swoole\Table 或 Redis 中,但映射键必须带
worker_id,例如"uid:1002:worker_3",推送前先查对应 worker 的 server 实例 - TP 的
command定时任务本质是 CLI 进程,无法直接调用swoole_websocket_server实例,需通过Redis pub/sub或Unix Socket中转指令
onOpen 里做数据库查询阻塞连接?用 go() 协程或异步 MySQL
WebSocket 握手完成触发 onOpen,此时若同步查 MySQL 或调用 TP 的 Db::table()->find(),整个 Worker 进程会被卡住,后续所有连接请求排队等待——性能崩塌点往往藏在这里。
- 禁用任何同步 I/O:包括
file_get_contents、curl_exec、TP 的Http::get,它们都会让当前协程挂起 - 改用 Swoole 的
Co\Mysql或Co\Redis,或者用go(function () { ... })包裹耗时操作,确保主线程不阻塞 - ThinkPHP 8.0+ 的
think-swoole扩展支持协程适配,但需确认Db驱动已切换为swoole_mysql,否则Db::调用仍是同步的
部署后 WebSocket 400 或 502?重点查 Nginx 的 Upgrade 头透传
生产环境走 Nginx 反代时,WebSocket 升级请求(Upgrade: websocket)被过滤或改写,导致握手失败。错误现象通常是浏览器控制台报 Failed to execute 'send' on 'WebSocket': Still in CONNECTING state,或 Nginx 日志里出现 upstream sent no valid HTTP/1.0 header。
立即学习“PHP免费学习笔记(深入)”;
- Nginx 配置必须显式透传关键头:
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade"; -
proxy_http_version必须设为1.1,否则 Upgrade 头无效;proxy_read_timeout建议设为300以上,避免空闲连接被 Nginx 主动断开 - 如果用 CDN 或 WAF(如阿里云 DDoS 防护),确认其支持 WebSocket 协议升级,部分免费层会静默降级为 HTTP 短连接
真实线上环境里,WebSocket 和定时任务的耦合点从来不在代码多不多,而在于「谁在哪个进程里、数据在哪块内存里、头有没有被中间件吃掉」。这三个边界划不清,压测时连接数一上去,问题就不是报错,而是彻底静音。



















