workerman本身不内置自动广播或分组推送逻辑,仅提供基础websocket帧收发;需自行实现连接状态跟踪、screen_id标识、心跳保活及离线消息补发机制,否则易导致大屏掉线、数据不同步和性能瓶颈。

Workerman 本身不内置 WebSocket 服务端的“自动广播”或“按分组推送”逻辑,你得自己搭好连接管理 + 消息路由,否则大屏刷新会卡、掉线、数据不同步。
为什么直接 new Worker('websocket://0.0.0.0:2346') 不够用?
单纯启动一个 Worker 实例只负责收发原始帧,没有连接状态跟踪、没有客户端分组、没有心跳保活——大屏页面一旦刷新或网络抖动,onClose 不触发、连接残留、新连接无法继承旧状态。更麻烦的是:你没法区分哪个消息该推给哪块屏。
实操建议:
- 必须用
WebsocketConnection的$connection->id或自定义$_SESSION(配合Session扩展)标识每个大屏终端 - 用全局数组或
Redis存储“屏ID → connection 对象”的映射,避免用foreach($worker->connections)全量遍历(连接数一过百就明显延迟) - 在
onConnect里解析 URL 查询参数,例如wss://api.example.com?screen_id=dashboard-01,提取screen_id并绑定到$connection->screen_id
如何安全地向指定大屏推送 JSON 数据?
Workerman 的 $connection->send() 只接受字符串,但大屏前端通常 expect JSON。直接 json_encode($data) 发送没问题,但要注意三件事:
- 必须检查
$connection->isConnected()再 send,否则可能触发 warning:“trying to write on closed connection” - 避免在
onMessage里直接sleep()或阻塞 IO(比如同步查 MySQL),会导致整个进程卡住;改用AsyncTcpConnection或MySQLi::queryAsync(需 workerman/mysql) - 如果推送频率高(如每秒 10+ 条),启用
$connection->enableSSL = true前务必确认证书链完整,否则 Chrome 会静默断开 wss 连接且不报错
示例片段:
// 推送前校验
if ($connection->isConnected() && isset($connection->screen_id)) {
$connection->send(json_encode(['type' => 'stats', 'data' => $payload], JSON_UNESCAPED_UNICODE));
}
怎么让 50+ 大屏稳定长连不掉线?
默认配置下,Nginx、SLB、甚至某些企业防火墙会在 60 秒无数据时主动踢掉 TCP 连接。Workerman 不自动发 ping/pong,得手动加心跳。
- 在
onConnect设置定时器:$connection->pingTimer = Timer::add(25, function() use ($connection) { $connection->send('{"type":"ping"}'); }); - 在
onMessage里拦截ping/pong,并重置连接活跃时间(Workerman 本身不维护 lastActiveTime,得自己记) - Linux 内核参数要调:
net.ipv4.tcp_keepalive_time = 300(不能依赖它,仅作兜底) - 禁用
Worker::$daemonize = true时的max_connections默认值(2048),改用Worker::$maxConnections = 10000并配好 ulimit -n
真正难的不是推一条数据,而是当某块屏断网重连后,如何补发它离线期间的 37 条关键指标更新——这需要你在推送前把变更写入 Redis Stream 或本地 leveldb,并在重连时按 screen_id 拉取未读事件。这个环节没人替你做。











