必须用 Gateway::countOnline() 或 Redis 计数,禁用 $connection_count 全局变量;因多进程下各 worker 独立计数、重启归零、平滑重启时叠加丢失,导致统计失真。

直接结论:别用 $connection_count 全局变量,必须走 Gateway::countOnline() 或 Redis 计数,否则进程间数据不一致,高并发下统计值完全失真。
为什么 $connection_count++ 在多进程下会失效
Workerman 默认启动多个 worker 进程(比如 $worker->count = 4),每个进程维护自己的 $connection_count 变量。客户端连接只触发当前进程的 onConnect 回调,其他进程完全无感知。结果就是:
- 你看到的
$connection_count值只是单个进程的连接数,不是全局在线人数 - 重启某个 worker 进程,该进程的计数归零,但用户实际还在其他进程里连着
- 用
kill -USR1平滑重启时,新旧进程并存,计数叠加或丢失都可能发生
Gateway::countOnline() 是唯一可靠的内置方案
GatewayWorker 组件专为分布式连接管理设计,Gateway::countOnline() 底层通过 Gateway 进程广播查询所有 Register 进程注册的客户端状态,返回的是全集群真实在线数。前提是:
- 必须使用完整的 GatewayWorker 架构(Gateway + Register + BusinessWorker),不能只跑一个
Worker实例 - 客户端必须完成登录绑定,例如调用
Gateway::bindUid($client_id, $uid),否则未绑定的连接可能被忽略 - 确保
register进程正常运行,它是所有连接元数据的中心注册点
示例用法(在 BusinessWorker 中):
use GatewayWorker\Lib\Gateway;
$online = Gateway::countOnline(); // 返回整型数字,非字符串
echo "当前在线: {$online}\n";
用 Redis 实现跨框架/跨语言兼容的计数
如果你的项目混合了 Webman、Swoole 或前端直连 WebSocket,或者需要和 PHP-FPM 的登录态打通,Gateway::countOnline() 就不够用了。这时应改用 Redis 的原子操作:
- 在
onConnect里执行INCR online_users - 在
onClose或onError里执行DECR online_users - 务必加
EXPIRE online_users 30防止单点故障导致计数卡死 - 避免用
GETSET或轮询,高并发下INCR/DECR是唯一安全的
注意:Redis 计数反映的是「连接数」,不是「用户数」。如果一个用户开多个标签页,会算作多个在线。如需按用户去重,得用 SET 存 $uid,再用 SCARD 统计。
最容易被忽略的强退问题
浏览器强制关闭、断网、杀进程等场景下,客户端不会触发 onClose,Redis 或 Gateway 都无法自动感知掉线。解决方案只有两个:
- 启用心跳检测:在 Gateway 配置中设置
'ping_interval' => 25和'ping_not_response_limit' => 2,超时后 Gateway 主动踢出连接 - 业务层兜底:对长时间无消息的
$uid,定时任务用Gateway::isUidOnline($uid)或SISMEMBER核查并清理
没有心跳机制的在线统计,数值永远偏高——这是线上最常被忽视的偏差来源。

















