thinkphp原生不支持websocket,必须依赖workerman或swoole等常驻进程实现真正实时通信;http协议短连接无法维持状态、主动推送,轮询方案延迟高且易压垮服务。

ThinkPHP本身不内置WebSocket服务器,直接用thinkphp跑实时聊天必然卡在连接维持和消息广播环节——你得靠外部常驻进程补足这一块,否则所有“实时”都只是轮询或长轮询的假象。
为什么不能只靠ThinkPHP原生HTTP控制器做聊天
HTTP协议天生无状态、单向、短连接。用户发一条消息,index.php执行完就销毁整个请求上下文,根本没法主动推送给其他在线用户。哪怕你用file_get_contents或cURL去“通知”另一个客户端,也做不到毫秒级响应,更无法维持连接状态。
常见错误现象包括:
- 前端反复轮询
/api/message/poll,接口压垮MySQL和PHP-FPM - 消息延迟3–8秒,群聊里A发完B才看到,C根本收不到
- 用户刷新页面后,WebSocket连接断开但服务端没清理
$connections,内存泄漏
Workerman是目前最稳妥的接入方案
它不依赖Swoole扩展(兼容性更好),纯PHP编写,能直接集成进ThinkPHP项目目录,启动命令就是php think chat:server。关键是你不需要改业务逻辑代码——用户登录、好友关系、消息存库仍走ThinkPHP的UserModel和MessageModel,Workerman只管“连接管理+广播转发”。
实操要点:
- Workerman进程必须用
nohup php think chat:server &后台运行,不能放Web服务器里跑 -
Worker::$uidConnections要自己维护映射表,别依赖$_SESSION——HTTP会话和WebSocket连接生命周期完全不同 - 前端WebSocket地址不能写
ws://localhost:8080,得统一走Nginx反代:wss://yourdomain.com/ws,否则HTTPS页面连不上ws
消息落地与广播必须拆开两步做
用户提交消息,先由ThinkPHP的MessageController->send()存入MySQL并返回message_id;再通过Redis发布(publish chat:room:123 $json)触发Workerman订阅者消费。这样既保证数据持久化可靠,又避免数据库直连阻塞IO。
容易踩的坑:
- 把
Db::insert()和$worker->connections循环广播写在一个HTTP请求里——高并发下MySQL写满、连接池打爆 - 用
foreach ($worker->connections as $conn)逐个$conn->send(),而不是用Worker::broadcast()批量投递,吞吐量掉一半 - 没加心跳检测(
onWebSocketConnect里设ping定时器),NAT网关超时断连后连接对象残留
上线前必须验证的三个边界点
本地开发通不代表线上可用。真正卡住交付的往往是这些细节:
- Nginx配置漏了
proxy_set_header Upgrade $http_upgrade,导致Connection: upgrade头被过滤,WebSocket握手400 - 阿里云/腾讯云安全组默认禁用非80/443端口,
2345或5678端口连不通,得手动放行 - Workerman子进程崩溃后不会自动拉起,得配
supervisord或systemd守护,否则半夜掉线没人知道
真正的难点不在“怎么连上”,而在于“连上之后怎么稳住、怎么扩、怎么不出鬼”。消息队列选Redis还是Kafka,连接数到5000该切GatewayWorker分组,这些决策点往往在压测后才暴露——但架构上从第一天就得留出切换余地。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











