Workerman GatewayWorker 心跳需客户端、服务端、反向代理三层协同:Gateway::pingInterval 设25~30秒且pingData为合法JSON;Events::onMessage首行必更新$connection->lastMessageTime;需Timer::add主动清理超时连接;Nginx proxy_read_timeout须同步调大至86400。

Workerman GatewayWorker 的心跳不是配个参数就完事的,必须客户端、服务端、反向代理三层同步动,漏一层就会断连。
Gateway::pingInterval 和 pingData 必须设对
Gateway 进程的心跳配置是起点,但只改这两个还不够。常见错误是把 pingInterval 设成 60 秒,结果被 Nginx 或运营商 NAT 先砍了;或者 pingData 写成 "ping" 这种非 JSON 字符串,前端解析失败直接静默断开。
-
pingInterval建议设为25~30,不能大于中间件最短超时(如 4G 基站常为 60 秒) -
pingData必须是合法 JSON,例如'{"type":"ping"}',不能带 PHP 变量插值或换行 - 这个配置只影响 Gateway 进程自动发 ping 的行为,不替代业务层心跳逻辑
Events::onMessage 里必须无条件更新 lastMessageTime
很多人以为 Gateway 自动发 ping 就够了,其实服务端判断连接是否存活,靠的是每个 $connection->lastMessageTime 时间戳。而这个时间戳只能在 Events::onMessage() 里手动更新——而且必须第一行就写,不管消息是不是心跳、能不能 decode。
统一LLM网关 - 一个API对接70+AI模型,使用单一API密钥即可调用GPT、Claude、Gemini、Qwen、Deepseek、Grok等主流模型。
- 错误写法:
if (json_decode($msg, true)['type'] === 'ping') { $connection->lastMessageTime = time(); } - 正确写法:
$connection->lastMessageTime = time();—— 放在onMessage函数最开头 - 未绑定 UID 的匿名连接也得初始化该字段,否则定时器遍历时会触发 PHP Notice
要用 Timer::add() 主动清理超时连接
Workerman 不会自动关掉“假活”连接。Worker::$maxConnection 只管总数,不管真假。你得自己起一个定时器,周期性扫描并关闭超时连接。
- 检查周期建议设为心跳间隔的 1.5~2 倍,比如
pingInterval=25,则检查设为40秒 - 超时阈值建议 ≤90 秒(容忍丢一次心跳),别用
60硬卡,现实网络有抖动 - 清理前务必加
if ($connection->isConnected())判断,否则 race condition 下 close 已断连接会报 warning
Nginx proxy_read_timeout 必须同步调大
再勤快的心跳也救不了没改 Nginx 的配置。默认 proxy_read_timeout 60s 意味着:哪怕客户端每 25 秒发一次 ping,第 61 秒 Nginx 就直接 FIN 断连,服务端收不到任何通知,前端只会看到 WebSocket is already in CLOSING or CLOSED state。
- 必须在 location 块里显式设置:
proxy_read_timeout 86400; - 同时确认
proxy_send_timeout也设为同等值,避免响应 pong 被截断 - 如果用的是 AWS ALB 或腾讯云 CLB,注意它们不支持调大,得换用 NLB 或自建中转
真正容易被忽略的是:心跳逻辑必须侵入到每个数据入口点,而不是只在“收到 ping 时才动一下时间戳”。一旦漏掉某类原始二进制包、未 parse 的乱码消息、或协议升级过程中的握手帧,那个连接就会在下次扫描时被误杀。

















