尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

SpringCloud Alibaba无人售货柜实战(四):设备接入模块——设备注册、心跳上报、在线状态管理

SpringCloud Alibaba无人售货柜实战(四):设备接入模块——设备注册、心跳上报、在线状态管理
📅 发布时间:2026/8/2 1:28:22

SpringCloud Alibaba无人售货柜实战(四):设备接入模块——设备注册、心跳上报、在线状态管理

一台售货柜上电后怎么让服务器知道"我来了"?怎么判断它还活着?怎么知道它掉线了?这篇把设备接入的全流程讲透。

一、设备接入流程总览

无人售货柜不是普通的服务器,它是一台跑在RK3588工控板上的Android设备,通过MQTT和服务器通信。设备从上电到"在线可用",要经过四个阶段:

设备上电 → 连接MQTT Broker → 注册报到 → 心跳保活 → 在线服务

详细步骤:

  1. 工控板上电启动,Android App拉起
  2. 读取本地存储的设备SN码,连接EMQX MQTT Broker
  3. 连接成功后,向服务端发送注册请求(携带SN码)
  4. 服务端校验SN合法性,分配Token,更新设备状态为"在线"
  5. 设备每30秒发送一次心跳包
  6. 服务端定时扫描,90秒未收到心跳则标记离线

二、MQTT Topic设计

MQTT通信的核心是Topic设计。本项目采用{前缀}/{设备SN}/{动作}的层级结构:

Topic方向说明
device/{sn}/register设备→服务端设备注册
device/{sn}/heartbeat设备→服务端心跳上报
device/{sn}/status设备→服务端状态上报(开门/关门/故障)
device/{sn}/command服务端→设备下行指令(开柜/重启等)
device/{sn}/callback设备→服务端指令执行回调

每个设备订阅自己的device/{sn}/commandTopic,服务端往这个Topic发消息,设备就能收到。SN码天然就是路由Key。

三、设备注册流程

3.1 注册逻辑

设备首次上电(或Token过期后)需要注册:

  1. 设备读取本地SN码(出厂烧录,不可篡改)
  2. 拼接SN + 设备型号 + 固件版本,发送到device/{sn}/registerTopic
  3. 服务端收到消息,查device表确认SN存在且未被禁用
  4. 生成JWT Token,存入Redis(key=device:token:{sn},有效期7天)
  5. 更新设备状态为"在线",记录上线时间
  6. 通过MQTT返回Token给设备

3.2 注册Controller

设备注册走MQTT消息,不走HTTP。服务端用MQTT消息监听器处理:

@Slf4j@ComponentpublicclassDeviceRegisterListener{@AutowiredprivateDeviceServicedeviceService;@AutowiredprivateRedisUtilsredisUtils;@AutowiredprivateMqttGatewaymqttGateway;privatestaticfinalStringDEVICE_TOKEN_PREFIX="device:token:";privatestaticfinallongTOKEN_EXPIRE_DAYS=7L;/** * 监听设备注册Topic */@MqttMessageListener(topic="device/+/register")publicvoidonRegister(MqttMessagemessage){Stringtopic=message.getTopic();Stringsn=topic.split("/")[1];Stringpayload=newString(message.getPayload(),StandardCharsets.UTF_8);log.info("收到设备注册请求: sn={}, payload={}",sn,payload);DeviceRegisterReqreq=JSON.parseObject(payload,DeviceRegisterReq.class);req.setSn(sn);// 1. 校验设备合法性Devicedevice=deviceService.getBySn(sn);if(device==null){log.error("设备未登记: sn={}",sn);sendRegisterResult(sn,false,"设备未登记",null);return;}if(device.getStatus()==DeviceStatus.MAINTENANCE.getCode()){sendRegisterResult(sn,false,"设备维护中",null);return;}// 2. 生成TokenStringtoken=JwtUtils.generateDeviceToken(sn,device.getId());// 3. 缓存Token到RedisredisUtils.set(DEVICE_TOKEN_PREFIX+sn,token,TOKEN_EXPIRE_DAYS,TimeUnit.DAYS);// 4. 更新设备状态device.setStatus(DeviceStatus.ONLINE.getCode());device.setLastOnlineTime(LocalDateTime.now());device.setLastHeartbeatTime(LocalDateTime.now());device.setFirmwareVersion(req.getFirmwareVersion());device.setIpAddress(req.getIpAddress());deviceService.updateById(device);// 5. 返回注册结果sendRegisterResult(sn,true,"注册成功",token);log.info("设备注册成功: sn={}",sn);}privatevoidsendRegisterResult(Stringsn,booleansuccess,Stringmsg,Stringtoken){JSONObjectresult=newJSONObject();result.put("success",success);result.put("message",msg);if(token!=null){result.put("token",token);}mqttGateway.sendToMqtt("device/"+sn+"/register/resp",result.toJSONString());}}

四、心跳上报机制

4.1 心跳协议

设备每30秒上报一次心跳,内容非常精简:

{"sn":"VM202601001","timestamp":1753766400000,"cpuTemp":52.3,"memoryUsage":68,"diskUsage":45,"signalStrength":-67}

心跳不只是"我还活着",顺便带上设备健康指标。cpuTemp超过80度就该告警了。

4.2 心跳处理Service

@Slf4j@ServicepublicclassDeviceHeartbeatService{@AutowiredprivateDeviceServicedeviceService;@AutowiredprivateRedisUtilsredisUtils;privatestaticfinalStringDEVICE_ONLINE_PREFIX="device:online:";privatestaticfinallongONLINE_EXPIRE_SECONDS=90L;/** * 处理心跳消息 */@MqttMessageListener(topic="device/+/heartbeat")publicvoidonHeartbeat(MqttMessagemessage){Stringtopic=message.getTopic();Stringsn=topic.split("/")[1];Stringpayload=newString(message.getPayload(),StandardCharsets.UTF_8);HeartbeatReqreq=JSON.parseObject(payload,HeartbeatReq.class);// 1. 更新Redis在线状态(90秒过期)StringonlineKey=DEVICE_ONLINE_PREFIX+sn;redisUtils.set(onlineKey,"1",ONLINE_EXPIRE_SECONDS,TimeUnit.SECONDS);// 2. 更新数据库最后心跳时间(异步,不阻塞MQTT回调)Devicedevice=newDevice();device.setSn(sn);device.setLastHeartbeatTime(LocalDateTime.now());deviceService.updateHeartbeatBySn(sn,LocalDateTime.now());// 3. 异常告警检查if(req.getCpuTemp()!=null&&req.getCpuTemp()>80){log.warn("设备CPU温度过高: sn={}, temp={}℃",sn,req.getCpuTemp());// 发送告警通知...}log.debug("心跳处理完成: sn={}",sn);}}

心跳处理一定要快。MQTT消息回调是串行的,如果心跳处理慢了,后面的消息全部排队。所以DB更新用异步,核心逻辑只做Redis更新。

五、在线状态判定

5.1 双重判定机制

设备在线状态用Redis + 定时扫描双重保障:

  • Redis快速判定:device:online:{sn}这个Key存在就算在线,过期时间90秒。心跳每30秒续期,一旦心跳停止,Key自动过期。
  • DB定时扫描:兜底机制,防止Redis故障导致状态不一致。

5.2 定时状态扫描

@Slf4j@ComponentpublicclassDeviceStatusScanner{@AutowiredprivateDeviceServicedeviceService;@AutowiredprivateRedisUtilsredisUtils;privatestaticfinalStringDEVICE_ONLINE_PREFIX="device:online:";privatestaticfinallongOFFLINE_THRESHOLD_SECONDS=90L;/** * 每60秒扫描一次,将超时设备标记为离线 */@Scheduled(fixedRate=60000)publicvoidscanOfflineDevices(){log.info("开始扫描离线设备...");// 查询所有状态为"在线"的设备List<Device>onlineDevices=deviceService.listByStatus(DeviceStatus.ONLINE.getCode());intofflineCount=0;LocalDateTimethreshold=LocalDateTime.now().minusSeconds(OFFLINE_THRESHOLD_SECONDS);for(Devicedevice:onlineDevices){// 优先检查RedisBooleanisOnline=redisUtils.hasKey(DEVICE_ONLINE_PREFIX+device.getSn());if(BooleanUtil.isFalse(isOnline)){// Redis已过期,再检查DB最后心跳时间兜底if(device.getLastHeartbeatTime()==null||device.getLastHeartbeatTime().isBefore(threshold)){// 标记离线device.setStatus(DeviceStatus.OFFLINE.getCode());deviceService.updateById(device);offlineCount++;log.warn("设备离线: sn={}, lastHeartbeat={}",device.getSn(),device.getLastHeartbeatTime());}}}log.info("离线扫描完成: 在线={}, 新增离线={}",onlineDevices.size(),offlineCount);}}

5.3 设备状态机

注册成功 心跳超时/异常 OFFLINE ────────→ ONLINE ──────────→ OFFLINE │ │ 故障上报 重新注册 ↓ │ FAULT ←──────────────┘ │ 维修开始 ↓ MAINTENANCE │ 维修完成 ↓ OFFLINE → 重新注册 → ONLINE

六、设备信息缓存策略

设备信息是高频读取数据(每次下单都要查设备状态),直接查DB扛不住。缓存策略:

数据缓存KeyTTL说明
在线状态device:online:{sn}90秒心跳续期
设备信息device:info:{sn}30分钟DB变更时主动刷新
认证Tokendevice:token:{sn}7天注册时写入
设备锁device:lock:{sn}10秒防止并发操作同一设备
publicDevicegetDeviceBySn(Stringsn){Stringkey="device:info:"+sn;Devicedevice=redisUtils.get(key,Device.class);if(device!=null){returndevice;}device=this.getBySn(sn);if(device!=null){redisUtils.set(key,device,30,TimeUnit.MINUTES);}returndevice;}

七、常见问题

7.1 设备频繁上下线(网络抖动)

设备网络不稳定,一会儿上线一会儿掉线,状态频繁切换,产生大量日志。

解决方案:引入"离线延迟确认"机制。心跳超时后不立即标记离线,先进入"疑似离线"状态,再等一个心跳周期(30秒),如果还没心跳,才正式标记离线。

// 疑似离线缓存,30秒后过期redisUtils.set("device:pending_offline:"+sn,"1",30,TimeUnit.SECONDS);

7.2 心跳丢失

MQTT QoS设置为1(至少一次),但极端情况下仍可能丢消息。解决:

  • 设备端本地记录心跳序号,连续3次未收到服务端ACK就重连
  • 服务端用@Scheduled补偿扫描,以DB时间戳为最终判据

八、设备管理表补充

在第一篇的device表基础上,补充设备扩展信息表:

CREATETABLE`device_config`(`id`bigintNOTNULL,`device_sn`varchar(64)NOTNULLCOMMENT'设备SN',`heartbeat_interval`intNOTNULLDEFAULT30COMMENT'心跳间隔(秒)',`offline_threshold`intNOTNULLDEFAULT90COMMENT'离线判定阈值(秒)',`door_timeout`intNOTNULLDEFAULT120COMMENT'开门超时(秒)',`max_open_duration`intNOTNULLDEFAULT300COMMENT'最大开门时长(秒)',`auto_close_enabled`tinyintNOTNULLDEFAULT1COMMENT'是否自动关门',`volume`intNOTNULLDEFAULT50COMMENT'音量(0-100)',`create_time`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,`update_time`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,PRIMARYKEY(`id`),UNIQUEKEY`uk_device_sn`(`device_sn`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='设备配置表';

九、小结

设备接入是IoT项目的第一道关卡。核心就三件事:注册让设备拿到身份、心跳让服务端知道设备活着、状态扫描兜底处理异常掉线。MQTT Topic设计要提前规划好层级结构,后期改Topic成本极高。Redis做在线状态缓存是标配——DB扛不住每30秒一次的心跳写入。

相关新闻

  • 2026 年新消息:海淀热门的视觉检测工厂找哪家,用它查商品瑕疵,居然比老眼昏花的质检员还靠谱? - 行业推荐官[官方】--
  • 2026 年至今,策勒正规的泡池施工施工队怎么联系,20平小院装私汤竟省了3万?原来它藏着不为人知的细节 - 企业推荐官【认证官方】
  • GD32H7定时器单脉冲模式寄存器级配置与调试指南

最新新闻

  • 2026 年现阶段永和专业的管桁架全套杆件加工销售厂家竞争格局,这玩意儿居然能让工地的架体搭建省一半工时,没人比它更懂现场需求?-艺钧膜结构停车棚 - 企业信息推荐【官方】
  • RocketMQ 延迟消息实战:延迟双删策略解决 Redis 缓存一致性
  • Word表格高效生成序号的4种方法:从编号、公式到域代码全解析
  • 2026年8月湖南省邵阳市电信单宽带避坑全攻略 - 找卡家园
  • GD32F403 EXMC外部存储器控制器配置与调试全攻略
  • MOS管与BJT饱和区本质差异:从物理机制到电路设计的深度解析

日新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号