必须用swoole websocket绕开php-fpm短连接限制,确认协程支持(php≥7.4且swoole enable_coroutine)、绑定合法uid、启用心跳、用table或redis管理会话、异步task处理耗时操作、标签匹配智能分配客服,并实现前端指数退避重连与心跳保活。

要让PHP网站具备毫秒级响应的即时通讯能力,必须绕开PHP-FPM的短连接枷锁,用Swoole WebSocket服务器维持长连接,同时规避内存泄漏、连接堆积和消息丢失三类典型故障。
WebSocket服务端基础搭建
第一步:确认PHP环境支持协程。运行php -v检查版本≥7.4,再执行php --ri swoole | grep "enable_coroutine",输出enabled才可继续。
第二步:创建server.php,实例化Swoole\WebSocket\Server并监听非80/443端口,例如9501——【若绑定80端口且Nginx未配置WebSocket升级代理,客户端将始终返回HTTP 400错误】。
第三步:在onOpen回调中调用$server->bind($request->fd, $uid)绑定用户标识,此处$uid必须来自$request->get['token']或$request->header['authorization']校验结果,不可用随机数替代。
第四步:启用心跳检测,设置'heartbeat_check_interval' => 60, 'heartbeat_idle_time' => 300,否则闲置连接会在30分钟后被内核强制断开,导致客户端重连风暴。
会话与路由状态管理
方法一:单机部署用Swoole\Table
定义共享内存表:$table = new Swoole\Table(1024); $table->column('uid', Swoole\Table::TYPE_STRING, 64); $table->column('agent_fd', Swoole\Table::TYPE_INT, 8); $table->create();
用户上线时写入:$table->set('fd_' . $fd, ['uid' => $uid, 'agent_fd' => 0]);
客服接单后更新:$table->set('fd_' . $fd, ['uid' => $uid, 'agent_fd' => $agent_fd]);
方法二:集群部署用Redis Hash + Sorted Set
会话元数据存Hash:HSET session:1001 uid 1001 status online agent_id support_002
客服负载用ZSet:ZADD support:load 3 "support_001",数值为当前会话数,分配时用ZRANGEBYSCORE support:load 0 5取低负载者。
【删除离线记录必须在onClose回调中同步执行,否则Table或Redis残留“幽灵会话”,新用户进线会被错误路由】
消息分发与异步解耦
第一步:收到用户消息后,先解析JSON中的type字段——文本、图片、已读回执、切换客服等类型必须分流处理。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
第二步:纯文本与实时指令(如typing)由Worker协程即时推送:$server->push($target_fd, $msg_json);若目标fd不在线,跳过推送,直接进入下一步。
第三步:耗时操作交由taskworker异步执行——消息落库、触发短信通知、生成工单均调用$server->task($data),主Worker不等待返回。
第四步:离线消息写入MySQL+Redis队列,结构为INSERT INTO offline_msgs (session_id, content, created_at) VALUES (?, ?, ?),并用LPUSH queue:offline:1001 $json缓存待拉取内容。
客服智能分配策略
策略一:标签匹配优先
用户进线携带{"issue_type": "payment", "level": "vip"},客服列表筛选同时满足tags:['payment','vip']且current_session 者。
策略二:负载均衡兜底
当无标签匹配时,从Redis ZSet中取ZRANGE support:load 0 2前三名,排除status != online者,选score最小者分配。
策略三:超时自动转接
用户排队超120秒未接入,触发$server->task(['action'=>'escalate', 'session_id'=>$sid]),升权至高级客服组并推送提示:“您的请求已升级至VIP专线”。
前端连接与异常容错
初始化WebSocket时,URL必须带token参数:new WebSocket("wss://your-domain.com:9501?token=abc123"),服务端靠此校验合法性。
实现指数退避重连:首次失败延迟1秒,第二次3秒,第三次5秒,最大不超过30秒,避免瞬时重连压垮服务端。
心跳帧必须由前端主动发送{"type":"ping"},服务端收到后立即回{"type":"pong"},禁止用setTimeout模拟——浏览器后台标签页会冻结定时器。
消息发送前检查socket.readyState === WebSocket.OPEN,否则存入本地队列,待onopen事件触发后批量重发。










