workerman的gatewayworker架构可快速构建实时客服系统,通过常驻进程承载websocket连接,省去自研底层逻辑;需严格遵循官方目录结构、配置监听端口、使用utf8mb4字符集、缓存用户信息、fifo分配客服,并正确配置nginx代理。

要在生产环境中快速交付一套支持实时消息、访客分配与会话持久化的在线客服系统,Workerman 提供了开箱即用的 GatewayWorker 架构,绕过传统 PHP-FPM 的阻塞瓶颈,直接以常驻进程方式承载 WebSocket 长连接,省去自研连接池、心跳保活、路由分发等底层逻辑。
初始化 GatewayWorker 项目结构
进入你的项目根目录,执行以下命令拉取官方 GatewayWorker 安装包:
composer create-project workerman/gateway-worker --prefer-dist
这一步会生成标准的 Applications/YourApp 目录结构,包含 Events.php、BusinessWorker.php、Gateway.php 三个核心入口文件。不要手动新建或重命名这些文件——GatewayWorker 启动时会严格按路径加载,改名会导致 Worker 进程启动失败。
删除默认的 Applications/YourApp/Config 下的 config.php,用你自己的配置替换:监听地址设为 0.0.0.0:8282,内部通信端口设为 2929,确保网关与业务进程能通过 TCP 正确握手。
配置数据库连接与消息存储逻辑
在 Applications/YourApp/Events.php 中,于 onMessage 方法内插入 MySQL 插入语句:
$pdo = new PDO('mysql:host=127.0.0.1;dbname=kefu;charset=utf8mb4', 'root', 'password');
$stmt = $pdo->prepare("INSERT INTO chat_messages (session_id, sender_type, message, created_at) VALUES (?, ?, ?, NOW())");
$stmt->execute([$data['session_id'], $data['sender_type'], $data['content']]);
注意:PDO 必须使用 utf8mb4 字符集,否则 emoji 和四字节生僻字会变成问号或截断。MySQL 服务端也需同步设置 character_set_server = utf8mb4,否则仅客户端声明无效。
不要在 onMessage 中做耗时查询——比如每次收消息都查一遍用户昵称。把昵称缓存在 Redis 或内存数组里,用 $connection->nick = '张三' 方式挂载到连接对象上,读取只需 $connection->nick。
实现访客自动分配到空闲客服
第一步:在 Applications/YourApp/BusinessWorker.php 的 onWorkerStart 中初始化一个全局空闲客服列表:
global $idle_agents; $idle_agents = [];
第二步:当客服上线(type=login)且 status=online 时,将其 worker_id 加入 $idle_agents 数组;下线(type=logout)时移除。
第三步:访客发起会话请求(type=start_session)时,从 $idle_agents 取出第一个元素,调用 $gateway->sendToUid() 推送分配指令,并更新会话表 status 为 'active'。
这一步不能用随机分配——高峰期多个访客同时请求,若每次都 rand($idle_agents),可能反复分配给同一个客服,造成负载倾斜。必须用 array_shift() 实现 FIFO 队列式轮询。
前端建立 WebSocket 连接并发送消息
方法一:原生 JavaScript 直连
const ws = new WebSocket('ws://your-domain.com:8282');
ws.onopen = () => ws.send(JSON.stringify({type: 'login', uid: 'visitor_123'}));
ws.onmessage = e => console.log(JSON.parse(e.data));
方法二:使用 Workerman 官方推荐的 workerman-js 客户端库
npm install workerman-js
import { WsClient } from 'workerman-js'; const client = new WsClient('ws://your-domain.com:8282');
client.send({type: 'message', session_id: 'sess_xxx', content: '你好,请问有什么可以帮您?'});
注意:若部署在 Nginx 后,必须配置 WebSocket 升级头,否则连接会被 400 拒绝:
location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
启动服务并验证连接状态
执行启动脚本:
php start.php start -d
检查日志是否输出 Gateway started 和 BusinessWorker started —— 两者缺一不可,Gateway 负责接入,BusinessWorker 才处理业务逻辑。
用浏览器开发者工具 Network 标签页观察 WebSocket 连接状态,Status 应为 101 Switching Protocols;若显示 pending 或 failed,说明 Nginx 代理未透传 upgrade 头,或防火墙屏蔽了 8282 端口。











