Workerman 是Linux小主机上实现低成本高可靠密室物联网联动的最可行PHP方案,关键在于正确绑定定时任务到Worker生命周期、用Redis实现多进程状态同步与指令队列、软件消抖及断电后自动恢复安全态。

Workerman 本身不内置“密室机关控制协议”,但它是目前在 Linux 小主机(比如千里通盒子)上实现低成本、高可靠物联网联动的最可行 PHP 方案——关键不在它“能做什么”,而在它“怎么接硬件”和“怎么保活”。
定时任务必须绑定 Worker 进程生命周期,否则一重启就失联
密室机关(如电磁锁、LED 灯带、语音播报模块)依赖稳定心跳,不能靠 cron 或短生命周期脚本驱动。Workerman 的 onWorkerStart 是唯一安全注册点:
-
Timer::add()必须在$worker->onWorkerStart内调用,且建议用static $inited = false; if (!$inited) { ...; $inited = true; }防止多进程重复注册 - 如果用
system('echo 1 > /sys/class/gpio/gpioXX/value')控制树莓派/千兆通 GPIO,需确保 PHP 进程有gpio组权限,否则Permission denied - 避免在定时回调里做阻塞操作(如
file_get_contents请求未加超时的 HTTP 接口),会卡死整个事件循环
WebSocket 实时接收剧本杀 DM 指令,比轮询更稳
DM 手机 App 或中控面板发指令(如“开启B区雾化器”“播放第3段音效”),直接走 WebSocket 推送,比 HTTP 轮询低延迟、少资源:
- 启动一个
WebsocketWorker监听0.0.0.0:2346,用$connection->send()向指定客户端广播 - 指令格式建议统一为 JSON:
{"action":"trigger_lock","device_id":"door_b2","params":{"delay":2000}} - 收到指令后,不要直接执行硬件操作,先写入 Redis 队列(
redis->lPush('device_queue', $json)),再由另一个 Worker 消费——解耦指令接收与执行,防指令堆积
多设备状态同步必须用共享存储,别信内存变量
密室常有多个 Worker 进程(例如 4 个),每个进程的 $worker->xxx 属性互不相通。想让“B区门已开”状态全局可见,不能靠进程内变量:
- 用 Redis 的
SET door_b2:status "open"+EXPIRE设置过期时间,避免断电后状态残留 - 所有定时任务读状态前,先
GET;执行动作后,立刻SET新状态并PUBLISH到 Redis channel,供其他服务监听 - 切忌用
apcu_store()或opcache存状态——它们只在单进程内有效,多进程下完全不可靠
物理信号抖动必须软件消抖,别指望继电器自己扛
按钮、红外对射、压力板等开关类传感器接入 GPIO 后,机械抖动会导致 Timer::add(0.05, ...) 反复触发多次。真实项目里必须加防抖:
- 用 Redis 的
SETNX door_btn:last_trigger "1" EX 1实现 1 秒内去重(EX 1是关键) - 或在 PHP 层维护一个
static $lastTriggerTime = [];数组,按设备 ID 记录最后触发时间戳,差值小于 300ms 直接 return - 硬件层加 RC 电路是治本,但多数密室改造没条件重布线,软件消抖是刚需
真正难的不是写几行 Timer::add,而是让 PHP 进程在断网、断电、看门狗重启后,能自动恢复所有设备到“安全态”(比如全部上锁、灯全灭),这个逻辑得写进 onWorkerStop 和守护脚本里——而大多数方案根本没考虑这点。

















