webman不原生支持mqtt,需通过“设备→mqtt broker→webman消费端”架构实现iot通信;其websocket服务需手动实现心跳、退避重连及redis等外部存储状态,避免多进程数据不一致与告警风暴。

Webman 本身不是专为 IoT 设计的框架,但它的高性能、常驻内存和 WebSocket 支持,让它能稳当地承担设备监控后端角色——关键不在“能不能”,而在“怎么绕过它不原生支持 MQTT 的短板”。
为什么不能直接用 webman 接收设备原始数据
绝大多数 IoT 设备(尤其是嵌入式终端)默认走 MQTT 协议上报数据,而 Webman 没有内置 MQTT Broker,也不监听 1883 端口。硬改设备固件切 HTTP POST 成本高、风险大;直接让设备连 Webman 的 HTTP 或 WebSocket 接口,则面临连接维持难、QoS 无保障、离线消息不可达等问题。
所以真实路径是:设备 → MQTT Broker(如 Mosquitto / EMQX)→ Webman 消费端。Webman 不做“接入层”,只做“处理+分发层”。
- 设备保持标准
MQTT连接,发布到类似/device/{id}/status的主题 - 用独立进程(如 PHP 脚本或 Python 服务)订阅该主题,收到消息后通过
WebSocket或HTTP主动推给 Webman 内部逻辑 - Webman 的
WebSocket服务(如app\process\WebSocket.php)负责把状态广播给已连接的浏览器客户端
WebSocket 连接管理必须自己补全心跳与重连
Webman 的 WebSocket 服务默认不带心跳检测,IoT 场景下设备端或网关网络不稳定,TCP 连接容易静默断开,导致前端长时间显示“在线”假象。
你得在 onMessage 回调里手动处理 ping/pong:
$ws->onMessage = function ($connection, $data) {
$msg = json_decode($data, true);
if ($msg['type'] === 'ping') {
$connection->send(json_encode(['type' => 'pong', 'ts' => time()]));
return;
}
// 其他业务逻辑
};
同时前端必须主动发 ping(例如每 25 秒一次),并监听 onclose 触发指数退避重连——Webman 不管前端连没连上,只管自己端口是否在 listen。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 别依赖浏览器
WebSocket自动重连,它不带退避逻辑,断连风暴会打崩服务端 - 服务端不要用
connection->close()清理“疑似断连”连接,应靠onClose回调触发清理 - 每个设备 ID 建议绑定唯一
connection实例,避免多页面重复连接造成状态错乱
设备状态更新不能直写 $_SESSION 或全局变量
Webman 是多进程模型,$_SESSION 默认基于文件,跨 worker 不共享;全局变量在 fork 后各自独立,改了等于没改。你想查“设备 A 当前温度”,不能存到某个 worker 的内存里就完事。
必须落地到进程外存储:
- 高频读写(如每秒一条心跳)→ 用
Redis的SET device:A:temp "24.6" EX 60,带自动过期 - 需查询历史趋势(如过去 1 小时温湿度曲线)→ 写入
InfluxDB或TimescaleDB,别塞 MySQL - 设备上下线事件广播 → 用 Redis Pub/Sub,让所有 Webman worker 都能订阅
device:online频道
如果你图省事用 file_put_contents 写 JSON 文件,高并发下大概率丢数据或报 Permission denied ——多个 worker 同时 open/write 同一个文件,Linux 文件锁不是万能的。
监控告警触发要防抖,别让一条异常数据狂发 10 条邮件
传感器偶发噪声很常见。比如温度探头瞬间飘到 99℃,实际是干扰。如果代码写成“一收到 >60℃ 就调 mail()”,运维半夜会被告警轰炸醒。
合理做法是加两级缓冲:
- 第一级:Redis 的
INCR+EXPIRE,10 秒窗口内超阈值次数 ≥3 才算真异常 - 第二级:告警发送后,设个
alarm:device:A:temp:blocked键,有效期 5 分钟,期间相同告警直接跳过 - 前端展示时,用
requestAnimationFrame节流 UI 更新,避免每秒 20 条数据把 Vue 组件卡死
真正的难点从来不是“怎么推数据”,而是“怎么判断该不该推”——这个判断逻辑必须可配置、可回溯、可关闭,否则监控系统本身就会变成故障源。










