不能在onMessage或onConnect中直接调用系统ping命令,因其同步阻塞会卡住Workerman单线程事件循环,导致消息积压、心跳失效、连接雪崩及status异常;必须用子进程+异步信号+超时控制实现非阻塞探测。

不能在 onMessage 或 onConnect 里直接调用系统 ping 命令轮询硬件设备,会阻塞事件循环;必须用子进程 + 异步信号调度 + 超时控制三者配合,否则所有连接都会卡住。
为什么不能用 exec('ping -c 1 192.168.1.100') 直接探测
Workerman 的 Worker 进程是单线程事件循环,exec、system、shell_exec 等函数默认是同步阻塞的。哪怕目标设备响应慢一点(比如丢包重传、ARP 缓存未命中),整个 Worker 就会卡在那几秒,导致:
• onMessage 消息积压、延迟飙升
• 心跳检测失效,误判大量连接为“掉线”
• 客户端重连请求堆积,触发雪崩式连接风暴
• status 页面显示 busy 进程数持续上涨,total_request 停滞
用 pcntl_fork + posix_kill 实现非阻塞 ping 探测
核心思路:把耗时、不可控的网络探测交给独立子进程,主 Worker 只负责发信号、收结果、做业务判断。
- 在
onWorkerStart中 fork 一个子进程,该进程只干一件事:监听信号,收到SIGUSR1就执行一次ping -c 1 -W 2 192.168.1.100 - 子进程用
proc_open启动 ping,并设置stream_set_timeout($pipes[1], 2)防止卡死 - 主进程用
Timer::add(15, function() { posix_kill($pid, SIGUSR1); })每 15 秒触发一次探测 - 子进程探测完把结果(成功/失败/耗时)写入 Redis 或共享内存,主进程异步读取并更新设备在线状态
如何避免 ping 被防火墙或设备静默丢弃
很多工业设备(如 Modbus 网关、DTU)默认禁 ICMP,或只允许特定源 IP 的 ping。光靠 ping 返回值不可信,必须叠加验证:
- 先用
arping -I eth0 -c 1 192.168.1.100查 ARP 表是否存在对应 MAC 地址(局域网更可靠) - 再尝试建立 TCP 连接(如
fsockopen('192.168.1.100', 502, $errno, $errstr, 1)),Modbus 设备通常开放 502 端口 - 如果两者都失败,才标记为“疑似离线”,连续 3 次失败再触发告警或重连逻辑
- 记录每次探测的原始输出(如
ping的time=xx ms或100% packet loss),用于后续分析链路抖动
真正难的不是发 ping,而是让 ping 的结果能被 Workerman 的事件循环及时感知且不拖垮服务——子进程生命周期管理、信号传递可靠性、超时后子进程是否残留,这些细节出问题,比 ping 不通本身还难排查。

















