Webman 不支持 MQTT,需用 workerman/mqtt 在独立 Process 中管理连接;HTTP 控制器内新建客户端会导致阻塞、断连、丢消息;状态同步须经 Redis 中转,WebSocket 需手动实现心跳与退避重连。

Webman 本身不支持 MQTT 协议,不能直接让设备连它、也不能在 HTTP 控制器里 new 一个 php-mqtt/client 就完事——这么做轻则断连漏消息,重则卡死整个 Worker 进程。
为什么不能在控制器里直接用 php-mqtt/client
常见错误是把 MQTT 客户端写在 app/controller 下的某个方法里,每次 HTTP 请求都新建连接。这会导致:
-
php-mqtt/client是阻塞 I/O 模型,会打断 Webman 的事件循环,整个 Worker 卡住几秒甚至更久 - Broker 连接数被快速耗尽(比如 EMQX 默认单 IP 限制 100 连接)
- 没有自动重连机制,Broker 重启后连接永久失效
- 无法复用连接,
onMessage回调风格和 Workerman 不兼容,得自己轮询,极易丢消息
必须用 workerman/mqtt 启动独立 Process
唯一稳定方式是把 MQTT 订阅逻辑放到 app/process 目录下的独立进程里,由 Workerman 管理生命周期。关键点:
- 继承
support\Process,在onWorkerStart中创建\Workerman\Mqtt\Client实例 - 配置
reconnect_period(建议设为8),避免 Broker 临时不可用导致进程僵死 -
onConnect回调里再调用subscribe(),防止连接未建立就发 SUBSCRIBE 包 - 收到消息后别直接查库或 send() 给 WebSocket,应投递到
redis()->lPush('mqtt_incoming', ...)或用Worker::sendToWorker()转交
示例片段中 $mqtt->subscribe('device/+/status') 是动态通配符订阅,但注意:Process 启动后不会自动响应设备列表变化,增删设备需重启进程或额外加 Redis Pub/Sub 触发重订阅逻辑。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
WebSocket 广播前必须过 Redis 中转
设备状态更新不能写进 $_SESSION 或全局变量,因为 Webman 是多 Worker 模型,内存不共享。正确链路是:
- MqttProcess 收到
/device/1001/status→ 写入SET device:1001:status "{...}" EX 60 - 同时
PUBLISH device:1001:update "{...}" -
app/process/WebSocket.php用Redis::subscribe(['device:*:update'])监听,确保所有 Worker 都能收到变更 - 前端连接通过唯一
device_id绑定$connection,避免同一设备多个页面重复连接、状态错乱
前端 WebSocket 必须自己实现心跳与退避重连
Webman 的 WebSocket 服务默认不带心跳,TCP 连接静默断开后 $connection->isConnected() 仍返回 true,导致“假在线”。所以:
- 服务端在
onMessage里识别ping并回pong,但不要主动 close 连接 - 前端每 25 秒发一次
{"type":"ping"},并监听onclose触发指数退避重连(如 1s → 2s → 4s → 8s) - 别依赖浏览器原生
WebSocket的自动重连,它没退避逻辑,网络抖动时可能瞬间打崩服务端
真正难的不是连上 MQTT,而是让设备、Broker、MqttProcess、Redis、WebSocket、前端六层之间状态一致——任何一层掉链子,都会表现为“数据延迟”或“状态错乱”,而这类问题往往只在压测或弱网环境下才暴露。

















