设备发来的消息怎么处理?物联网后端消息链路拆解:Topic 约定 + 三层拦截 + 原始数据兜底

发布时间:2026/10/8 19:37:43

设备发来的消息怎么处理?物联网后端消息链路拆解:Topic 约定 + 三层拦截 + 原始数据兜底 前面的文章把设备怎么连上来一机一密 ACL、断线了怎么办自动重连、遗嘱消息都讲过了。但有一条主线一直没完整写过一条遥测消息从 EMQX 到达服务端之后、落进数据库之前到底经历了什么这条链路是物联网后端的主干道。面试里说说你的平台怎么处理设备消息出现频率极高因为这一个 problem 就能问出你是不是真做过Topic 怎么设计的、消息怎么校验、脏数据怎么办、为什么这么分层。这篇就把我的真实代码摊开讲。一、先约定 Topic它就是设备端的接口契约我平台的消息处理入口是TelemetryMessageHandler.java文件头注释里写着整个系统的主题约定/** * 主题约定 * up/{productKey}/{deviceId} 遥测数据payload 为 JSON * st/{productKey}/{deviceId} 设备状态payload 为 online/offline遗嘱消息用 offline */只有两条主题、三层路径。为什么设计得这么抠因为 Topic 在物联网里就是接口契约——它是设备端和后端共同遵守的 URL定下来就不能随便改。层级越少设备端固件越好写后端解析越不容易出错通配符订阅也越方便服务端只要订阅up/#和st/#两条就能收全所有产品的所有设备。做后端的可以这么类比Topic 是 URL pathpayload 是 request bodyQoS 是要不要重试。把 REST API 设计里契约先行的纪律原样搬过来就行——先定 Topic 和 payload 格式再写解析代码顺序不能反。二、回调函数里第一件事别让一条消息弄死连接消息从 EMQX 推到服务端进入的是 Paho 客户端的回调messageArrived。我在MqttConnection.java里是这么写的OverridepublicvoidmessageArrived(Stringtopic,MqttMessagemessage){StringpayloadnewString(message.getPayload(),StandardCharsets.UTF_8);try{handler.handle(topic,payload);}catch(Exceptione){// 单条消息处理失败不能影响连接和后续消息log.error(处理消息失败: topic{},topic,e);}}重点是这个 try-catch。messageArrived跑在 Paho 客户端单条的回调线程上业务代码在这里抛异常后果不只是丢一条消息——处理流程卡住后续消息全部排队甚至影响心跳和连接稳定性。所以回调里只做两件事转码、委托给业务 handler并且任何异常都必须就地吞掉记日志。连接参数上我也做了取舍这几行在面试里被追问的概率很高options.setAutomaticReconnect(true);options.setCleanSession(true);options.setMaxInflight(100);// 订阅时int[]qos{1,1};QoS 用 1遥测数据允许重复但不允许丢cleanSession 用 true服务端掉线期间的旧消息不强求补齐因为看板要的是当前状态几秒前的旧温湿度补回来反而是脏数据。这是业务属性决定的选型不是拍脑袋。三、三层拦截认证、鉴权、业务校验各拦各的真正进业务逻辑之前每条消息要过三道门。第一道在 EMQX一机一密认证 ACL前面有文章专门写过但后端代码里还有两道DevicedevicedeviceRepository.findById(deviceId).orElse(null);if(devicenull||!productKey.equals(device.getProductKey())){log.warn(收到未知设备的数据已丢弃: topic{},topic);return;}// 已禁用的设备即使 Broker 层有漏网的ACL 未生效/缓存入库前再拦一道。// 安全上的原则认证、鉴权、业务校验三层各拦各的不假设上一层一定生效。if(DeviceService.STATUS_DISABLED.equals(device.getStatus())){log.warn(设备[{}]已禁用丢弃其消息: topic{},deviceId,topic);return;}为什么要重复拦截因为每一层防的风险不一样**第一层认证**防你是谁都没搞清——伪造 clientId、偷来的密钥**第二层ACL**防合法设备发不该发的主题——但 ACL 有缓存、有生效延迟**第三层业务校验**兜住前两层的一切漏网被禁用的设备、topic 里 deviceId 和 productKey 对不上的伪造请求。这段代码注释里那句话是我踩坑之后的总结“不假设上一层一定生效”。分布式系统里每一层都可能有缓存、延迟、配置漂移把安全押在前面那层应该拦住了上迟早出事故。四、先存原始 JSON再谈解析过了三道门的遥测消息我的处理方式可能和很多教程不一样——不做字段级解析原始 payload 直接落库privatevoidhandleTelemetry(Devicedevice,StringproductKey,Stringpayload){// 校验是合法 JSON不合法直接丢弃防脏数据进库try{objectMapper.readTree(payload);}catch(Exceptione){log.warn(payload 不是合法 JSON丢弃: {},payload);return;}device.setStatus(online);deviceRepository.save(device);DataPointdataPointnewDataPoint();dataPoint.setDeviceId(device.getId());dataPoint.setProductKey(productKey);dataPoint.setPayload(payload);// 原样保存不拆字段dataPoint.setReceivedAt(Instant.now());dataPointRepository.save(dataPoint);}两个细节。第一入库前用readTree做一次轻量校验只确认这是合法 JSON不提取任何字段——防的是固件 bug 吐出来的半截数据污染表。第二payload字段存的是原始字符串收到时间单独记一列。为什么这么设计因为设备端是最不可控的一环固件有 bug、传感器会吐异常值、协议后面还会升级。解析逻辑一定会变但原始数据错了就永远错了。只要原始 JSON 在以后想加字段、改解析规则、修历史数据重放一遍就行。做数据平台的人都懂一句话最值钱的不是解析后的结果是带时间戳的原始数据。另外注意device.setStatus(online)这行——收到数据本身就是最可靠的在线信号这比心跳轮询更及时离线判定靠遗嘱消息 超时兜底那是另一篇的事了。五、面试怎么答这道题如果面试官问你们平台设备消息是怎么处理的我会用一条链路 三个关键词回答设备按约定的 Topicup/产品/设备ID发 JSON 到 EMQX服务端订阅后在 messageArrived 回调里转码并委托给业务层业务层过三层校验设备存在、未禁用、合法 JSON然后把原始 payload 带时间戳落库同时更新设备在线状态。三个关键词契约Topic 先行设备端后端共同遵守、兜底回调吞异常保连接、三层拦截保安全、原始数据先落库再解析为未来的自己留后路。能顺着这条线把为什么讲清楚基本就能让面试官相信消息链路这条主干道你是真的修过。作者人设软件工程在读专升本正在从零搭一个物联网设备接入平台Spring Boot 3.5 EMQX MySQL把踩的每个坑都写成文章。上一篇写了《接口报错全返回 500Spring Boot 全局异常处理与统一响应体实战》欢迎关注追更下一站是消息管道的吞吐升级。
延伸阅读

更多相关文章

2026/10/8 19:37:43

信息论基础学习笔记

《信息论基础》翟明岳|第一章 绪论 学习笔记前言:信息论之父——克劳德香农克劳德艾尔伍德香农(Claude Elwood Shannon,1919—2001),美国数学家,信息论的创始人,被誉为“信息时代之父…

2026/10/8 20:43:01

哈佛教授AI科研框架:Claude Code+GitHub+Python实战指南

1. 从"物理教授跨界刷题"说起:这个AI科研框架到底在解决什么第一次看到"哈佛物理教授用Claude三个月横扫18个领域36个难题"这个说法,我的反应是:又是一个标题党。但仔细拆解背后的逻辑,我发现真正值得关注的不…

2026/10/8 20:43:01

高校就业服务小程序源码复盘:Spring Boot+微信小程序完整业务闭环

从“可白嫖源码”这个标签点进来的朋友,大概率是想找一份能跑通、能看懂、能改着玩的高校就业服务小程序。这个07523项目我整体过了一遍,前端是微信小程序,后端带完整业务接口和数据库脚本,不是网上那种只有几个页面的半成品&…

2026/10/8 20:43:01

Java面试硬核考点:严肃面试官与搞笑程序员的高能对决

面试这件事,我一直觉得是个双向表演。面试官在表演“我很专业”,候选人在表演“我很懂行”,奈何总有人演技过于浮夸,或者过于真实。我在老东家做技术面试官那几年,面过形形色色的Java后端候选人,其中有一类…

2026/10/8 20:43:01

ChatGPT Java 工程化实战:异步、重试、限流与降级

1. 从“能跑”到“好用”:Java 项目里引入 ChatGPT 的真实分界线很多人第一次把 ChatGPT 接进 Java 项目时,心态都差不多:调通一个接口,返回一段文本,控制台打印出来,截图发群里,收工。但真正在…

2026/10/8 20:38:00

text-to-cad落地实战:LLM+OpenSCAD让一句话变成STL模型

前几天客户丢过来一句话需求:“做一个M8的六角头螺栓,总长50,螺纹长30,表面发黑。”搁以前,我第一反应是打开CAD软件,拉伸、旋转、倒角、切螺纹,一套操作下来少说十几分钟,要是再碰上…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑