用workerman搭建实时数据采集系统需分层解耦:串口设备须用独立进程监听防阻塞,tcp设备需自定义协议防粘包,http设备要限流防刷;数据经redis缓冲分流后,再异步落库并websocket广播。

用Workerman搭建实时数据采集系统,核心是让硬件设备上报的数据不堆积、不丢包、不阻塞主事件循环,同时能按业务规则分流处理。这不是写个监听端口就完事,而是要分层解耦:采集层只管收,解析层做清洗,业务层做判断,存储层保落地。
第一步:选对通信协议并隔离阻塞操作
确认你的硬件走哪种通路——串口设备(如老式RFID读卡器)必须用独立进程监听,不能在Workerman主进程里 fopen('/dev/ttyUSB0');TCP设备(如现代网关、POS机)可直接用Worker监听,但必须自定义协议防粘包;HTTP设备(如部分IoT传感器)走Workerman\Http\Server,但需加限流防刷。
串口监听推荐用Python子进程:启动一个单独的serial_reader.py,持续读取/dev/ttyUSB0,校验波特率9600-8-N-1后,把原始帧通过TCP发给Workerman的127.0.0.1:23456;【切勿在onWorkerStart里阻塞式打开串口,否则整个Worker进程永久卡死】。
TCP设备若用定长头+变长体协议,必须继承\Workerman\Protocols\ProtocolInterface重写input()方法:先读前4字节获取body长度,再等待凑够指定字节数才触发onMessage,否则两条刷卡记录可能被拼成一条乱码。
第二步:数据接收与轻量预处理
方法一:TCP直收(适用于网关、考勤机)
新建Worker('tcp://0.0.0.0:23456'),在onMessage中只做三件事:JSON_decode校验、提取card_id/epc/ts字段、推入Redis List(如rfid:raw);【严禁在此处查MySQL或调外部API,耗时超过5ms就会拖垮整个连接池】。
方法二:HTTP回调收(适用于Webhook型传感器)
用Workerman\Http\Server监听8080端口,在onRequest里用file_get_contents('php://input')获取原始body,立即用json_decode($raw, true)解析,过滤掉无epc或无ts字段的脏包,再lPush到同一Redis队列。
方法三:MQTT桥接收(适用于物联网平台)
启用Workerman\Mqtt\Client连接emqx或mosquitto,订阅主题sensor/#,收到消息后提取payload,同样清洗后进Redis;注意MQTT QoS设为1,避免网络抖动导致消息丢失。
第三步:用Redis做缓冲与分流
第一步:用eval原子脚本每200ms从rfid:raw取5条,ltrim截断,返回数组;这比单纯lpop+for循环更抗并发。
第二步:对每条数据做白名单比对——查Redis Hash staff:by_card,键为epc值;查不到就lPush到rfid:unknown,供人工复核;查到但status为inactive,直接丢弃不入库。
第三步:命中白名单后,用SETNX写入attendance:20260625:{epc},EX 300防止当日重复打卡;同时publish到channel task_20260625_a,触发WebSocket广播。
第四步:多终端实时广播与落库
前端手持机连WebSocket时,调用GatewayClient::bindUid($uid)绑定用户,再bindGroup('task_20260625_a')加入任务组;后端收到新标签后,立刻sendToGroup('task_20260625_a', json_encode([...])),前端onmessage更新界面计数。
落库操作必须异步:用pcntl_fork起子进程,或投递到Gearman队列;INSERT语句带UNIQUE KEY (pos_id, trace_no),用INSERT IGNORE忽略重复;每次$db->query()后必须判if ($insert_id === false),否则MySQL连接异常时数据静默消失。
离线缓存策略:手持机断网时本地SQLite暂存,恢复后POST到/api/batch_sync,Workerman用curl_multi并发提交,每批≤50条,超时设为3秒。











