从零自建物联网平台:MQTT接入、数据处理与可视化全攻略

从零自建物联网平台:MQTT接入、数据处理与可视化全攻略
最近在折腾一个给自家设备用的物联网接入平台从选型到部署再到设备真正跑起来前前后后踩了不少坑也积累了不少心得。正好有朋友问起“怎么搭一个属于自己的物联网平台”索性把这次完整的实操过程整理成一篇博文从架构思路到每一行关键配置都过一遍希望能帮到正在规划自建物联网后台的开发者。先说清楚“属于自己的物联网平台”到底指什么。市面上主流的云物联网平台确实强大但设备数据全部经过第三方服务器长期使用要考虑单设备连接费用、消息数量计费、数据存储费用而且平台规则和数据接口都在别人手里想深度定制报表或算法时往往处处受限。自建平台则意味着三件事通信层自己管、数据自己存、展示自己定。核心价值不只是省钱而是数据主权和系统弹性设备规模从几十台到几万台都在自己掌控之下想怎么扩展就怎么扩展。这篇内容的核心关键词可以拆成五个维度设备接入、消息传输、数据处理、可视化展示、平台运维。适合的读者画像也很清晰有一定开发基础、不想被云厂商绑定、希望搭建一套可长期演进的物联网基础设施的团队或个人开发者。1. 整体架构设计与技术选型思路1.1 自建物联网平台的完整链路拆解物联网平台本质上是一套处理“设备侧”和“应用侧”双向消息的基础设施。设备侧负责采集和上传数据应用侧负责下发指令和呈现数据。一个最小可用但具备生产价值的平台需要具备以下四个核心模块。设备接入层是所有通信的入口解决“设备如何连上来”的问题。这一层必须支持海量长连接同时要具备鉴权能力防止陌生设备乱入。消息处理层负责把设备上报的数据解析成统一格式并根据业务规则做出响应例如触发告警、写入数据库。数据存储层需要支撑高频写入和按时间维度的高效查询关系型数据库可以做但到一定量级会吃力。可视化与运维层面向人机交互既要能用仪表盘看实时数据也要能回溯历史曲线。从通信协议的角度看小到温湿度传感器、大到工业网关MQTT依然是物联网设备接入的事实标准它基于发布订阅模式消息开销极小在弱网环境下表现稳定。这样一套链路的软硬件映射关系可以理解为MQTT Broker 负责接入层Node-RED 负责消息处理层时序数据库负责存储层Grafana 或 Dashboard 负责展示层。举一个生活化类比如果把物联网平台比作一个物流公司MQTT Broker 就是收发货物的分拣中心所有快递设备消息先到这里再按地址分发Node-RED 是处理包裹的流水线工人负责拆包、检查、分类、贴标签入库时序数据库就是仓库按时间顺序存放每一件包裹而可视化界面是公司的查询窗口客户输入单号就能知道货物的全部轨迹。1.2 为什么选择 MQTT 作为设备接入协议在实际做平台选型时很多人会在 MQTT、HTTP 轮询、TCP 自定义协议之间犹豫。这里直接说结论对绝大多数物联网场景MQTT 是优先级最高的选择原因有三点。第一是极低的消息开销。MQTT 的控制报文头部最小只需 2 字节相比之下 HTTP 请求光是 Header 就有几百字节。对使用 NB-IoT、2G 或信号不稳定的 4G 模块设备来说省下的每一次流量都是真金白银信号差的时候小报文也更不容易被丢弃。第二是双向实时通信。HTTP 更擅长设备主动上报但服务器要主动下发指令给设备时就很别扭需要轮询或另建长连接。MQTT 的发布订阅模型天然支持双向通信平台向某个主题发布一条消息订阅了这个主题的设备能立刻收到。远程控制、OTA 升级指令下发这类场景只能靠这种机制来实现。第三是离线消息与遗嘱机制。MQTT 支持会话保持设备断线重连后可以收到离线期间的消息遗嘱消息则能在设备异常掉线时自动通知平台这对设备在线状态管理极其重要。协议本身还支持三级 QoS从“最多一次”到“恰好一次”不同业务场景可以灵活选择。HTTP 在物联网中依然是补充角色例如设备定期上报不带实时性要求的批量文件、或设备固件包下载等但在核心设备接入层MQTT 是更合适的基座。1.3 核心软件选型对照与理由具体组件选型时我的诉求很明确尽量站在巨人的肩膀上选开源、社区活跃、部署轻量的方案避免重复造轮子。以下是本次平台的核心软件选型。MQTT Broker 选择了 EMQX它是目前开源社区最成熟的 MQTT 消息服务器之一单机可以轻松承载百万级连接支持 MQTT 3.1.1 和 5.0 协议还内置了规则引擎可以把消息直接转发到其他服务。相比之下Mosquitto 更轻量但扩展能力弱适合几十台设备以内的极简场景。消息处理层选择了 Node-RED它最大的价值是可以用可视化方式编排消息流。设备上报的数据往往需要解析、格式转换、字段映射、判活、告警等一系列逻辑用 Node-RED 拖拽连线就能搞定而且它对 MQTT、HTTP、数据库都有现成节点。数据存储选择了时序数据库 TDengine。传统 MySQL 要自己处理按时间分区分表、保留策略、聚合查询而 TDengine 天生为时序数据设计建表后写入即可按时间窗口聚合十年前的数据也只需一条 SQL 就能基于降采样快速图形化。数据量小的话InfluxDB 2.x 也是很好的选择但从超大数据量下的压缩率和运维成本看TDengine 的超级表模型在物联网场景更有优势。可视化层选择了 Grafana 搭配 Node-RED Dashboard。Grafana 负责历史数据分析和监控大屏Node-RED Dashboard 负责实时调试和简单控制面板。两套方案互补不需要再引入一套重量级前端框架。还有一个容易被忽略的组件是 Nginx。它会前置在 EMQX 和 Node-RED 之前负责 SSL 终结、反向代理和端口映射设备端只需要连接一个 443 端口就能安全接入这能省去很多嵌入式防火墙配置的麻烦。2. 平台部署全流程与核心功能实现2.1 硬件环境准备与服务器规划平台部署需要一台具有公网 IP 的云服务器。配置方面如果是几百台设备以内的规模2 核 4GB 内存起步即可如果设备量达到几千或上万建议直接上 4 核 8GB并把数据库单独部署到另一台机器。带宽方面MQTT 本身对带宽占用很低但要注意如果有大量图片或 OTA 包下发需求带宽要吃紧得多需要另行规划。服务器操作系统建议选择 Debian 12 或 Ubuntu 22.04 LTS。这里有一个关键建议所有组件不论大小一律用 Docker 容器部署。自建平台最大的隐形负担就是运维Docker 能把每个组件的依赖打包隔离升级、回滚、迁移都在分钟内完成。只要在服务器上装好 Docker 和 Docker Compose 插件即可。实操中务必先规划好端口和域名否则后续修改很痛苦。基础端口划分建议如下MQTT TLS 端口 8883 用于设备接入HTTP 服务端口 8080 和 1880 用于 Dashboard 与 Node-RED443 端口由 Nginx 统一反代暴露 HTTPS 服务。数据目录也需要提前规划。建议在服务器上建立一个/opt/iot作为项目根目录内部按emqx、nodered、tdengine、grafana、nginx分别存放各自的配置和数据卷映射这比 Docker 默认的匿名卷管理方式要清晰得多。2.2 Docker Compose 编排全部核心服务Docker Compose 是本次平台部署的主心骨。用一份docker-compose.yml文件就能把 EMQX、Node-RED、TDengine、Grafana 全部编排起来一键创建整个平台下面给出一份我在生产环境中验证过的编排文件。version: 3.8 services: emqx: image: emqx/emqx:5.8.0 container_name: iot-emqx restart: always ports: - 1883:1883 - 8883:8883 - 18083:18083 environment: - EMQX_DASHBOARD__DEFAULT_PASSWORD请修改为强密码 - EMQX_AUTHENTICATION__1__MECHANISMpassword_based - EMQX_AUTHENTICATION__1__BACKENDb Built-in Database volumes: - /opt/iot/emqx/data:/opt/emqx/data - /opt/iot/emqx/log:/opt/emqx/log nodered: image: nodered/node-red:3.1.9 container_name: iot-nodered restart: always ports: - 1880:1880 environment: - TZAsia/Shanghai volumes: - /opt/iot/nodered:/data tdengine: image: tdengine/tdengine:3.2.3.0 container_name: iot-tdengine restart: always ports: - 6030:6030 - 6041:6041 volumes: - /opt/iot/tdengine/data:/var/lib/taos - /opt/iot/tdengine/log:/var/log/taos grafana: image: grafana/grafana:11.1.0 container_name: iot-grafana restart: always ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORD请修改为强密码 - GF_INSTALL_PLUGINStdengine-datasource volumes: - /opt/iot/grafana:/var/lib/grafana这份编排文件里包含了几处我踩过坑后总结的关键设定。EMQX 默认 Dashboard 端口虽然只绑定在服务器上没有用 Nginx 暴露到公网但生产环境必须修改默认密码否则扫描器分分钟就能登录。EMQX 5.x 的认证配置和 4.x 差异很大早期版本用EMQX_AUTHENTICATION__1__...配置内置数据库认证5.8 版本则建议先把默认的允许匿名关闭否则设备端没有用户名密码也能接入。TDengine 的数据卷一定要单独挂载出来首次启动它会自动初始化数据目录如果容器意外删除而数据没挂载所有历史数据会全部丢失这是最痛的教训之一。启动命令也很简单在/opt/iot目录下执行docker compose up -d容器全部进入正常运行状态后先不要急着配置而是等待一分钟让各服务完成初始化然后依次访问 EMQX Dashboard 的 18083 端口、Node-RED 的 1880 端口和 Grafana 的 3000 端口确认页面能打开再进入下一步配置。2.3 EMQX Broker 的初始化与设备接入认证配置设备接入认证是物联网平台的第一道安全门。生产环境必须开启客户端认证禁止匿名连接。EMQX 5.x 的 Dashboard 操作路径比较直观左侧菜单进入“管理”下的“认证”然后添加一个“密码认证”数据源选择内置数据库认证方式选择 username/clientid。由于当前最新的 5.x 版本 Dashboard 界面细节会有变化最稳妥的方式是直接通过 API 配置认证。这里提供一段基于/api/v5接口与 curl 命令的创建脚本方便你快速初始化# 先在 /opt/iot 下创建认证配置目录 mkdir -p /opt/iot/emqx/etc # 使用 curl 创建 Password-Based 认证数据源为内置数据库关闭匿名认证 curl -sS -u admin:你的强密码 -X POST \ http://127.0.0.1:18083/api/v5/authentication \ -H Content-Type: application/json \ -d { mechanism: password_based, backend: built_in_database, authenticate: { algorithm: sha256, password: 固定盐值 }, user_id_type: username }为了操作准确也可以直接在配置文件方式下编辑emqx.conf来设置最低安全基线。EMQX 5.x 允许在运行中通过 Dashboard 关闭“允许匿名认证”开关。实操时我习惯直接把允许匿名设置为 false再创建测试设备和正式设备账号。设备账号建议遵循一套规范把设备 ID 作为用户名的一部分。例如一台温湿度传感器的设备编号为dev-0001可以为它创建用户名dev-0001密码单独生成一个随机字符串并记录到设备台账中。这里不建议所有设备使用同一个密码因为一旦泄露攻击者就能仿冒任意设备上报假数据。使用 Dashboard 内置数据库认证后每个需要接入的设备都要在“认证-用户管理”中创建一个对应用户。设备侧连接信息如下服务器地址你的域名或IP端口8883TLS或1883明文仅内网测试用户名dev-0001密码单独生成的随机密码Topic 前缀device/dev-0001/...2.4 Nginx 反向代理与 TLS 证书配置,让设备安全接入物联网设备分布在各种网络中很多网络环境会拦截非标准端口流量。因此生产平台强烈建议给 MQTT 启用 TLS 并分配 8883 端口由 Nginx 或 EMQX 直接处理证书。如果使用云服务器且有域名用 Let‘s Encrypt 申请免费证书是很顺手的方案。我的部署习惯是把证书放在 Nginx 上统一管理用stream模块做 TCP 层反向代理这样 EMQX 本身不直接接触证书文件证书的更新维护都在 Nginx 一处搞定。Nginx 的 stream 配置片段如下stream { upstream mqtt_backend { server 127.0.0.1:1883; } server { listen 8883 ssl; proxy_pass mqtt_backend; ssl_certificate /etc/letsencrypt/live/你的域名/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/你的域名/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; } }这套配置需要 Nginx 编译时包含--with-stream和--with-stream_ssl_module使用系统包管理器装的 Nginx 通常都带。设备连接域名时MQTT 客户端库会校验证书是否匹配域名如果直接用 IP 连接则需要额外处理证书校验逻辑这也是为什么建议准备一个域名的一个原因。开发调试阶段如果暂时没有域名可以先用明文 1883 配合内网调试但上生产绝不建议明文传输数据。2.5 Node-RED 数据流编排与设备接入实战Node-RED 在这一平台中扮演“业务逻辑大脑”的角色。设备上报的数据通过 EMQX 传入 Node-RED 后,经过解析、清洗、转换最终写入 TDengine。下面以一款实际温湿度传感器设备为例展示从 MQTT Topic 原始 JSON 数据到入库的完整流程。设备上报的原始消息格式通常如下{ deviceId: dev-0001, ts: 1731484800, temp: 23.6, humi: 48.2, battery: 89 }Node-RED 中需要建立一条 flow拆解步骤如下MQTT In 节点订阅主题device/dev-0001/dataqos 设为 0输出原始消息字符串。JSON 节点把字符串解析为 JavaScript 对象。Function 节点做数据校验检查字段是否齐全、数值是否在合理范围例如温度在 -40 到 100 之间。校验不通过直接 return null 丢弃。TDengine 写入节点通过 RESTful 接口将数据插入表中。Node-RED 中还需要部署一条对设备下行指令的 flow。当用户从 Dashboard 点击“打开继电器”时MQTT Out 节点向主题device/dev-0001/cmd发布{cmd: relay_on}设备端订阅该主题后执行动作并回复执行结果。整个流程从数据接入到指令下发形成一个闭环不再需要手工改代码。3. 设备接入的实现细节与数据链路打通3.1 设备端 MQTT 连接代码实现与参数选择设备端重点是稳定连接与自动重连能力。网络不稳定的物联网设备一旦断线后不能自动恢复整个平台的数据就断了。如果设备端使用 ESP32 或类似硬件平台基于 PubSubClient 库的连接代码中必须设置几个关键参数连接服务器时使用mqttClient.setServer(host, 8883)、启用自动重连回调、设置保活时间 keepalive 为 60 秒、通过setBufferSize调大接收缓冲区。保活时间的设置值得仔细提一下。MQTT 客户端在 keepalive 时间内必须向 Broker 发送一次 PINGREQ 心跳如果 Broker 在 1.5 倍 keepalive 时间内没有收到任何报文就判定设备离线并清理会话。keepalive 太短会增加网络功耗和信令开销太长则会让平台对设备离线感知变得迟钝。一般传感器场景建议 30 到 60 秒需要实时监控设备在线状态的场景可以缩短到 10 到 15 秒。设备端的完整连接伪代码如下void connectMqtt() { while (!mqttClient.connected()) { Serial.print(MQTT connecting...); String clientId dev-0001; if (mqttClient.connect(clientId.c_str(), dev-0001, 设备密码, device/dev-0001/status, 1, true, offline)) { Serial.println(connected); mqttClient.subscribe(device/dev-0001/cmd, 0); publishStatus(online); } else { delay(3000); } } }注意connect函数中传入的遗嘱主题和遗嘱消息。设备断线后Broker 会自动向device/dev-0001/status主题发布一条offline的遗嘱消息这样平台可以实时感知设备离线而不需要依赖超时。设备正常联网后主动发布online状态构成一套完整的在线状态上报机制。3.2 Topic 设计规范与权限隔离Topic 是 MQTT 消息的路由地址设计好坏直接决定后续业务能否灵活扩展。若设备数量过百随意设计的 Topic 结构会带来灾难性的维护成本。推荐使用三段式或四段式结构分类/设备ID/行为/扩展。以我的实际项目为例完整的 Topic 规划如下设备上行数据device/deviceId/data设备上行事件device/deviceId/event设备在线状态device/deviceId/status平台下行指令device/deviceId/cmd平台批量广播broadcast/upgrade每台设备只允许发布和订阅自己前缀下的主题不能访问其他设备的主题。EMQX 中通过内置数据库认证每个设备用户名可以绑定 ACL 规则只放行特定主题前缀。没有 ACL 隔离的物联网平台任何一台设备被攻破就等于全部设备沦陷这一点需要防范于未然。设备端也需要订阅平台广播主题比如升级通知。广播主题使用通配符broadcast/upgrade所有设备都能收到但在业务逻辑层由设备自行判断是否匹配自身型号和版本。3.3 时序数据库建表与写入策略,让数据存得清爽TDengine 的建模思路和传统数据库有明显区别。核心概念是超级表和子表一类设备建一张超级表每一台具体设备建一张子表子表名一般直接使用设备 ID。创建一个温湿度传感器的超级表 SQL 如下CREATE STABLE IF NOT EXISTS sensor_data ( ts TIMESTAMP, temperature FLOAT, humidity FLOAT, battery INT ) TAGS ( device_id BINARY(64) );每台设备第一次上报时动态创建子表并将其与超级表关联CREATE TABLE IF NOT EXISTS dev_0001 USING sensor_data TAGS (dev-0001); INSERT INTO dev_0001 VALUES (NOW, 23.6, 48.2, 89);TDengine 的高效之处在于后续查询不需要关心具体有哪些表直接对超级表做聚合即可。例如查询某台设备最近一小时的平均温度和最大湿度SELECT AVG(temperature), MAX(humidity) FROM sensor_data WHERE device_id dev-0001 AND ts NOW - 1h;数据保留策略可以在创建数据库时指定例如保留 180 天数据超期自动删除CREATE DATABASE iot_data KEEP 180 DURATION 10 BUFFER 64 WAL_LEVEL 2;3.4 Node-RED 到 TDengine 的数据链路打通方式Node-RED 与 TDengine 对接有两条常用路径通过 RESTful 接口用 HTTP Request 发送 SQL或使用社区提供的 taos-node 节点。我在多次实践中推荐最稳定的方案在 Node-RED 中直接使用 HTTP Request 节点请求 TDengine 的 6041 端口。用 HTTP Request 写入一条数据之前需要先完成数据库和子表的初始化。在 Node-RED 中要先执行建库建档操作然后再走数据写入流程。一条设备数据的落库流程拆解如下。MQTT In 节点收到 JSON 后Function 节点将原始数据转换为 TDengine 参数化查询所需格式再把 INSERT 语句和 token 拼好通过 HTTP Request 请求 TDengine RESTful 接口执行 SQL。用 curl 测试 RESTful 写入的打开方式如下curl -u root:taosdata -X POST http://127.0.0.1:6041/rest/sql \ -H Content-Type: text/plain \ -d INSERT INTO dev_0001 VALUES (NOW, 23.6, 48.2, 89)Node-RED 中 Function 节点可以先把收到的 JSON 转化为参数字符串再传递给 HTTP Request 节点。检查响应体如果包含code: 0说明写入成功如果返回数据库不存在或权限错误需要检查 TDengine 初始账号和建库语句。3.5 Grafana 仪表板与实时监控配置数据存进 TDengine 之后可视化展示是物联网平台给用户最直观的部分。Grafana 需要先添加 TDengine 数据源注意 TDengine 的 Grafana 插件要求数据源 URL 指向 RESTful 端口 6041并填写数据库用户名密码和默认数据库。创建仪表板时一个重要的技巧是使用 Grafana 变量功能。定义一个变量device_id查询语句直接以超级表为基础SELECT DISTINCT device_id FROM sensor_data;这样仪表板顶部出现设备下拉框选中不同设备即可切换查看不同设备的数据曲线。面板中温度曲线对应的查询语句SELECT AVG(temperature) FROM sensor_data WHERE device_id $device_id AND $timeFilter GROUP BY $timeWindow;Grafana 的$timeFilter和$timeWindow会自动适配仪表板右上角选择的时间范围和刷新粒度这是时序展示的核心便利之处。再配上阈值告警规则比如温度超过 60 度时触发 Alertmanager 通知整个平台的运行监控体系才算真正建立起来。告警方面Grafana 8.0 之后内置了告警引擎。创建一个告警规则设置查询条件为“最近 5 分钟温度最大值 60”触发后通过 Webhook 调用钉钉或企业微信机器人接口即可把告警推送到手机。4. 高频踩坑记录与运维排错实战4.1 设备端始终连接不上 Broker 的排查路径设备端连接被拒是物联网平台最常见的问题。通常最先检查的是网络连通性在设备所在的局域网电脑上用telnet 服务器IP 8883测试端口是否可达。如果端口不通优先检查云服务商的安全组规则是否放行了 8883 端口再检查 Nginx stream 模块是否正常监听。端口通但 MQTT 依然连不上就要转向检查认证信息。EMQX 开启认证后如果用户名密码错误Broker 会直接断开连接。看 EMQX 日志是最快的定位手段docker logs iot-emqx --tail 100日志中如果出现Client dev-0001 authentication failed就说明认证没通过。注意检查用户名是否带空格、密码是否错误、密码是否在 Dashboard 创建时被复制多了一个引号。如果设备上报数据正常但平台收不到先查看 Node-RED 中 MQTT In 节点是否订阅了正确的主题。实测中偶发的现象是设备端代码把主题拼成了device/dev-0001/data/最后一个多余的斜杠而 Node-RED 订阅的是device/dev-0001/dataMQTT 主题对字符匹配是精确的这类低级错误会导致消息永远无法到达。4.2 设备经常性掉线与心跳、网络环境的关系设备经常掉线是另一个典型痛点。首先要确认设备的网络信号强度。如果是 Wi-Fi 设备2.4GHz 频段比 5GHz 穿墙能力好但容易受微波炉、蓝牙设备干扰。如果信号覆盖不好设备就容易频繁断线重连这类问题只能从物理链路侧入手调整设备位置或增加网关。信号没问题还掉线就要看 keepalive 时间与设备网络状态的匹配。如果设备每次休眠超过 keepalive 时间网络层已经被运营商回收但应用层并不知道要等到 MQTT 心跳超时才触发重连。此时可以在设备端把 keepalive 设置为 30 秒同时把 MQTT 断开后的重连间隔设为指数退避策略例如首次 5 秒之后 10 秒、20 秒、40 秒上限 5 分钟。这样在信号恢复后能第一时间重连同时避免大量设备同时重连对 Broker 造成的重连风暴。一类特殊场景是移动蜂窝网络。4G 模块在部分运营商网络中会周期性释放长时间空闲的连接即使应用层心跳正常也可能被网络层强制断开。这类问题的规避方式是平台侧主动在业务低峰期发送下行 ping让模块在一段时间内有持续流量避免被判定为空闲连接。4.3 4G/5G物联网卡的实际问题与应对策略这次搭建平台过程中物联网卡相关的问题是最大的意外来源。市面上所谓的“纯流量卡”或“设备卡”分两种一种是正规运营商发行的物联网卡通过企业实名开卡走物联网专用 APN另一种是个人手机卡伪装成的“流量卡”用在设备上非常不稳定。平台侧必须能够同时兼容这两种卡的接入环境。第一个典型问题是没有公网 IP。大量物联网卡走的是运营商 CGNAT运营商级网络地址转换大内网设备拿到的 IP 是 100.64.x.x 之类的运营商保留地址能访问外网但外部无法直接反向连接设备。这正好解释了为什么 MQTT 这类“设备主动外连 Broker”的长连接架构更适合物联网设备发起的 TCP 连接经过 NAT 后依然能维持双向通信平台不需要知道设备的 IP。第二个典型问题是物联网卡有白名单限制。企业物联网卡一般需要在运营商管理后台配置“专用 APN”允许访问的服务器 IP 或域名白名单。如果平台所在的服务器 IP 没有配置进去设备即便 SIM 卡有信号TCP 连接也无法建立。处理方式是找物联网卡服务商把平台服务器的公网 IP 加到白名单。第三个典型问题是内置物联网卡不能随便插在手机上用。正规物联网卡不占用个人手机号实名资源很多还被运营商限制不能用于手机终端插入手机可能触发停机。调试阶段最好用一部专门的上网终端或在开发板上直接测试不要图方便插手机。4.4 数据存储与时间线相关问题的排查TDengine 相关的问题又细分成几类。最容易踩的是客户端时区与服务器时区不一致导致的时间偏移。设备上报数据如果带的是 UTC 时间而服务器使用 Asia/Shanghai 时区查询时数据会整体偏移 8 小时。解决方法是设备端统一上报 Epoch 毫秒时间戳不带任何时区概念时序数据库内部存储 UTC前端展示时再转换时区。在 Grafana 中直接把时区设置为 Asia/Shanghai 即可正常显示。另一类问题是 TDengine 数据库文件持续增长。如果只建数据库没有配置 KEEP 参数默认数据会一直保留。我的建议是建库时显式指定 KEEP 和 DURATION 参数保留策略从源头控制容量。关于超级表和子表的容量扩展TDengine 3.x 在集群规模方面表现更好但对单机版用户来说单块数据盘容量是主要瓶颈建议提前把数据卷挂在独立的数据盘上不要把服务器系统盘和数据库数据盘混在一起。4.5 安全性加固与平台日常维护清单自建平台的安全加固容易被忽视但一旦出事后果严重。第一步是禁止匿名 MQTT 连接所有设备凭用户名密码接入。第二步是为 EMQX Dashboard、Grafana、Node-RED 后台都设置强密码并开启登录限制。EMQX 5.x 还支持 Dashboard 监听地址配置只允许内网访问 Dashboard。在 docker-compose 中把 18083 端口只绑定到127.0.0.1然后通过 SSH 隧道或在 Nginx 层加一层 Basic Auth 后再暴露能有效避免 Dashboard 被公网扫描爆破。第三步是定时备份。对物联网平台而言最核心的备份数据是 TDengine 中的历史时序数据其次是 EMQX 中的设备账号和 ACL 规则、Node-RED 中的 flow 配置。TDengine 的备份可以用taosdump工具taosdump -o /backup/iot_data -D iot_dataNode-RED 的备份最简单因为整个flows.json就一个文件直接复制到外部存储即可。EMQX 的认证数据则可以通过 Dashboard 导出或定期调 API 备份。整套系统建议每周备份一次备份文件迁移到对象存储保存最近 30 天。日常巡检则建议做成一个小脚本每天检查 Docker 容器健康状态、磁盘占用率、以及 TDengine 的写入查询是否正常。只需要使用docker ps和df -h加一个简单的磁盘和容器异常判断即可实现自动告警避免平台故障了几个小时才发现。5. 这套平台的扩展方向与能力边界建好平台基础骨架后很多新需求会自然涌现。第一个能快速落地的扩展是设备 OTA 升级。在 Node-RED 中新增一个 flow 监听升级触发文件用 HTTP 上传固件到服务器并通过 MQTT 向目标设备广播升级指令设备端收到后开始下载固件包。原来云平台按设备数收费的 OTA 功能自建后可以轻松实现。第二个扩展方向是数据分析和机器学习。TDengine 存储了全量历史数据数据已经是标准 SQL 形态用 Python 或 Spark 可以直接从 TDengine 中读取数据训练模型例如设备故障预测、能耗异常检测这类应用。自建平台的开放性在这一步价值明显训练好的模型可以写成微服务嵌入 Node-RED 中作为新节点形成完整的预测与联动能力。第三个方向是多平台接入。平台自身使用 MQTT 标准协议不代表业务系统必须全部自己造。可以将平台内的数据通过 EMQX 规则引擎转发到外部大平台做备份展示也可以把业务系统收到的告警通过 Node-RED 转发到钉钉、企业微信等通信工具,保持灵活性的同时又不丢失现有生态的连接能力。需要清醒认识的是自建平台的能力边界在跨地域大规模设备组网、边缘计算、设备影子等高级功能上。如果业务达到一定规模设备分布在全国乃至全球多个地域、需要就近接入就需要重新考虑 EMQX 集群加边缘节点的部署方式。但在绝大多数中小规模的物联网项目里这套单机自建架构完全够用且给后期演进留足了空间。6. 经验总结与建议搭建“属于自己的物联网平台”最核心的收获不在代码层面而在对系统整体运行逻辑的掌控感。用云平台时出问题只能提工单等售后自建平台后可以从 Broker 日志一路排查到数据库 SQL每一个环节都能自己动手定位和优化这种能力积累在后续任何技术工作中都很有价值。在整套技术路线中个人最满意的是以 Docker Compose 为中心的管理方式。所有组件都通过一套编排文件定义平时运维只需要处理这一套配置。一台 2 核 4G 的云服务器承载这套平台可以稳定支撑上千台温湿度传感器每 30 秒上报一次数据的场景。如果你是刚开始尝试自建平台的新手建议不要在一开始就引入过多组件。先以 EMQX 加 Node-RED 加 SQLite 打通设备到展示的最小闭环确认整套链路跑通后再渐进替换为 TDengine 和 Grafana。这样可以避免初期架构过重也让每一步都能在可验证的前提下推进。本篇文章的完整方案可以把它当作平台从 0 到 1 的参考路线图按需裁剪、逐层落地即可。

最新新闻

日新闻

周新闻

月新闻