workerman 5.2.3 心跳检测需打通“数据入口—时间戳更新—超时判定—连接清理”四环节:onmessage首行无条件更新$connection->lastmessagetime;匿名连接也须初始化;超时阈值须严控在nat最短空闲时限内(如≤90秒);采用单一定时器+批量遍历模式,禁用每连接定时器。

Workerman 5.2.3 实现心跳检测,核心不是加个定时器那么简单,而是要打通“数据入口—时间戳更新—超时判定—连接清理”四个环节,并适配真实网络环境。版本本身不内置心跳逻辑,所有行为需开发者显式编码,且必须规避常见陷阱。
onMessage 第一行强制更新 lastMessageTime
这是最容易出错也最关键的一环。很多开发者只在成功解析业务消息后才写时间戳,导致心跳包(如 {"type":"ping"})因 JSON 解析失败被 return 掉,lastMessageTime 完全没更新,服务端 60 秒后直接误判离线。
正确做法是:无论 $msg 是什么格式、能否 decode、有没有业务含义,只要进入 onMessage,第一行就必须执行:
- $connection->lastMessageTime = time();
- 匿名连接(未 bindUid)也必须初始化,否则遍历时会触发 PHP Notice
- 建议用二进制前缀(如 \x01)或 WebSocket subprotocol 区分心跳与业务帧,避免依赖 JSON 字段判断
服务端超时阈值必须严控在 NAT 防火墙最短空闲时限内
Linux 内核的 tcp_keepalive_time=7200 对移动网络完全无效。运营商级 NAT、4G 基站、云厂商 ALB(如 AWS 固定 60 秒)普遍在 60–120 秒静默回收空闲连接。若服务端检查阈值设为 120 秒,等于默认允许丢两次心跳,风险极高。
推荐配置组合:
- 常规设备(手机/PC):客户端心跳间隔 ≤45 秒,服务端超时检查 ≤90 秒(容忍单次丢包)
- 低功耗终端(农业传感器):心跳 90 秒,服务端阈值 ≤150 秒
- 高可靠终端(远程控制面板):心跳 25 秒,服务端阈值 ≤75 秒,配合三次探测+指数退避
- 绝对禁止设 5 秒心跳——多数 4G 模组 AT 固件不支持,易卡死或流量暴增
定时检查必须用 Timer::add + 全局遍历,不能依赖最小堆自动触发
Workerman 5.2.3 底层 Timer 使用最小堆管理任务,但心跳检测不能为每个连接单独设一个定时器(内存和 CPU 开销爆炸)。应采用“单一定时器 + 批量扫描”模式:
- 在 onWorkerStart 中启动一个固定周期定时器(如每 40 秒),频率建议为心跳间隔的 1.5–2 倍
- 遍历 $worker->connections,对每个连接做 if (time() - ($connection->lastMessageTime ?? time()) > $timeout) { $connection->close(); }
- close 前务必加 if ($connection->isConnected()) 判断,防止 race condition 导致 warning
- 清理动作必须同步、无阻塞;禁止丢进协程队列或异步回调,否则 pong 延迟会引发客户端误判
Nginx 等中间件必须同步调大超时参数
Workerman 心跳再勤快,Nginx 默认 proxy_read_timeout 60s 也会在第 61 秒直接 FIN 断连,客户端收不到任何响应,只看到 WebSocket is already in CLOSING or CLOSED state。
必须同步调整反向代理配置:
- Nginx:设置 proxy_read_timeout 86400;(24 小时)
- 若用 AWS ALB,需换用 NLB 或自建 TCP 层代理
- CDN 或 WAF 节点也要确认其空闲超时策略,必要时绕过











