大家好,我是你们的老朋友-腻害兔,今天继续我们的 RuoYi-Vue-Pro(芋道)源码拆解系列。上一期我们聊了报表模块,今天来啃一块硬骨头——IoT 物联网模块(yudao-module-iot)

为什么说它是硬骨头?因为这个模块是整个 RuoYi-Vue-Pro 中架构最复杂、技术栈最多样、协议适配最多的模块,没有之一。它不是一个简单的 CRUD 模块,而是一个完整的 IoT 平台,涵盖了协议网关、物模型、设备管理、规则引擎、OTA 升级、告警系统等 IoT 核心能力。

废话不多说,直接上干货!


一、今日模块概览

一句话总结:IoT 物联网模块是一个支持多协议接入、多租户隔离、物模型驱动的全栈 IoT 平台。

它解决的核心问题是:让不同类型的物联网设备(MQTT、Modbus、CoAP、TCP/UDP、WebSocket、HTTP)能够通过统一的协议网关接入平台,通过物模型标准化数据结构,再通过规则引擎实现场景联动和数据流转。

整个模块分为三个子模块:

子模块 职责 部署方式
yudao-module-iot-core 公共基础设施:消息总线、枚举、工具类 共享 jar,部署在 gateway 和 biz 两端
yudao-module-iot-biz 业务逻辑层:设备管理、物模型、规则引擎、OTA、告警 业务服务器
yudao-module-iot-gateway 协议网关层:MQTT/Modbus/CoAP/TCP/UDP/HTTP/WebSocket/EMQX 独立的网关服务器(可水平扩展)

划重点!!! 网关和业务层通过消息总线(Message Bus)解耦,支持 Redis Stream、Kafka、RocketMQ、RabbitMQ 四种 MQ 实现,甚至可以退化为本地 Spring Event(单机模式)。这意味着你可以先单机跑起来,后续按需切换到分布式部署,架构弹性非常好。


二、技术选型分析

2.1 为什么用 Vert.x 而不是 Netty 做协议网关?

这是我在读代码时最好奇的一个问题。RuoYi 的主框架是 Spring Boot + Spring MVC,但网关层却选择了 Vert.x(vertx-mqtt、vertx-web),而不是直接用 Netty 或者 Spring 生态的 Spring Integration。

我的分析:

维度 Vert.x Netty Spring Integration
开发效率 高,内置 MQTT/HTTP/WebSocket/CoAP 支持 低,需要自己编解码 中等,配置繁琐
性能 高(基于 Netty) 最高 一般
多协议支持 开箱即用 需要自己实现 需要额外模块
与 Spring Boot 集成 有 vertx-spring-boot-starter 需要手动管理 原生
响应式模型 EventLoop 非阻塞 EventLoop 非阻塞 线程池模型

结论: Vert.x 本质上是 Netty 的上层封装,既保留了高性能,又提供了开箱即用的多协议支持。对于 IoT 场景需要同时支持 MQTT、HTTP、WebSocket、CoAP 等多种协议,Vert.x 的开发效率优势非常明显。而且通过 lichee:vertx-spring-boot-starter-web,Vert.x 可以嵌入 Spring Boot 一起运行,不需要独立部署。

2.2 为什么用 TDengine 而不是 InfluxDB / TimescaleDB?

设备消息和属性历史数据存储在 TDengine(时序数据库),而不是更常见的 InfluxDB 或 TimescaleDB。

我的分析:

维度 TDengine InfluxDB TimescaleDB
国产 vs 国外 国产(涛思数据) 国外 国外
超级表概念 原生支持,一个设备一张子表 不支持 不支持(普通表)
SQL 兼容性 兼容 SQL 自有查询语法 完全 PostgreSQL
写入性能 极高(专为 IoT 优化) 中等
社区生态 国内活跃 全球活跃 PostgreSQL 生态
JDBC 驱动 有(taos-jdbcdriver)

结论: TDengine 的超级表(Super Table)概念天然适合 IoT 场景——每个设备一张子表,同类型设备的超级表自动聚合查询。而且 TDengine 对 SQL 的兼容降低了学习成本,JDBC 驱动可以直接用 MyBatis-Plus 操作,和现有代码框架无缝集成。从 pom.xml 中看到引入了 taos-jdbcdriver,说明是通过标准 JDBC 方式访问的。

踩坑提醒: TDengine 的表结构是动态的——当物模型修改时,需要同步修改 TDengine 的超级表结构。代码里通过 syncProductPropertyTable 和 defineDevicePropertyData 方法实现了自动同步,这个设计很巧妙,后面会详细讲。

2.3 为什么消息总线支持 5 种实现?

消息总线(Message Bus)是连接网关和业务层的核心纽带。IotMessageBus 接口有 5 种实现:

实现 适用场景 特点
Local(Spring Event) 单机开发/测试 零依赖,但不支持多节点
Redis Stream 轻量级分布式 依赖 Redis,运维成本低
Kafka 高吞吐场景 百万级 TPS,适合大规模设备接入
RocketMQ 阿里生态 事务消息、延迟消息支持好
RabbitMQ 企业级消息 路由灵活,死信队列支持好

为什么不只支持一种? 因为 IoT 平台的部署规模差异极大。一个管理 100 台设备的智能楼宇系统,和一个管理 100 万台设备的工业物联网平台,对消息中间件的需求完全不同。RuoYi 的做法是:默认用 Redis(大多数场景够用),但保留了切换到专业 MQ 的能力。 这种"渐进式架构"的设计思路非常务实。

2.4 为什么用 MyBatis-Plus 而不是 JPA / Spring Data?

这个选择其实和 RuoYi 整体框架一致。MyBatis-Plus 在国内 Java 生态中占据绝对主流地位,原因是:SQL 可控、代码生成器好用、对复杂查询支持好。IoT 模块大量使用了 MyBatis-Plus 的 LambdaQueryWrapper、@TableField(JSON 类型处理器)等特性。


三、需求溯源推演

读完整套代码,我尝试还原这个 IoT 模块最初的产品需求文档大概长什么样:

3.1 核心需求推测

需求背景: 随着物联网的普及,企业需要一个统一的 IoT 平台来管理各种类型的智能设备。市面上的 IoT 平台(如阿里云 IoT、华为 IoT)虽然功能强大,但私有化部署成本高、定制灵活性差。开源社区缺少一个轻量级、可私有化部署、支持多协议的 IoT 平台。

核心用户角色:

角色 核心诉求
设备管理员 管理设备生命周期(注册、上线、下线、分组、删除)
产品管理员 定义产品模板和物模型(属性、服务、事件)
运维工程师 监控设备状态、处理告警、OTA 升级
集成开发者 通过数据流转将设备数据对接到第三方系统
规则配置员 配置场景联动规则(温度过高自动关阀)

3.2 需求演进推测

从代码的成熟度和功能完整性来看,这个模块经历了至少 3 个迭代周期:

V1.0(基础版): 产品管理 + 物模型 + 设备管理 + MQTT 接入 + 设备消息存储。这是 IoT 平台的最小可用集。

V2.0(增强版): 增加 Modbus/CoAP/TCP/UDP/HTTP/WebSocket 协议支持 + 场景联动规则引擎 + 数据流转 + 告警系统。这一步把 IoT 从"能接入"升级到"能联动"。

V3.0(完整版): 增加 OTA 固件升级 + EMQX 集成 + Modbus TCP Server + 设备动态注册 + 网关子设备拓扑管理 + 统计面板。这一步补齐了企业级 IoT 平台的必备能力。

产品思考: 这个模块的迭代路径其实是一个很好的 IoT 产品规划范本——先做"连接"(协议网关),再做"数据"(物模型+存储),然后做"智能"(规则引擎),最后做"运维"(OTA+告警)。每一层都建立在前一层的基础上。


四、竞品对标分析

IoT 开源平台赛道其实非常热闹,让我们把 RuoYi IoT 和几个主流竞品做个对比:

RuoYi IoT 的优势

  1. 生态集成度最高: 直接复用 RuoYi 的用户、权限、租户、字典、通知等基础设施,不需要额外对接。对于已经用 RuoYi 做后台管理系统的团队,加一个 IoT 模块就能用。

  2. 协议覆盖最全: 8 种协议支持,特别是 Modbus TCP Client/Server 双模式,这在工业 IoT 场景非常实用。很多开源平台只支持 Modbus 一种模式。

  3. EMQX 集成: 这是一个亮点。很多企业已经在用 EMQX 作为 MQTT Broker,RuoYi 通过 HTTP Hook + MQTT Client 的方式桥接 EMQX,避免了"换 Broker"的迁移成本。

  4. 多租户原生支持: 设备、产品、场景规则等核心表都继承了 TenantBaseDO,天然支持 SaaS 化部署。

RuoYi IoT 的劣势

  1. 规则引擎偏简单: 场景联动规则是简单的 Trigger-Condition-Action 模型,不支持 ThingsBoard 那种可视化的规则链编排(多个节点串联、函数计算、条件分支等)。

  2. 缺少可视化大屏: IoT 平台通常需要一个数据可视化大屏来展示设备地图、实时数据曲线、告警统计等。RuoYi 目前只有一个基础的统计 Controller。

  3. 设备影子/期望值: 阿里云 IoT 有"设备影子"功能——当设备离线时,云端缓存期望状态,设备上线后自动同步。RuoYi 目前没有这个能力。


五、核心业务流程

5.1 设备接入全流程

这是 IoT 平台最核心的流程——一台设备从"不存在"到"在线上报数据"的完整链路:

               

5.2 场景联动规则执行流程

5.3 OTA 升级流程

OTA(Over-The-Air)升级是 IoT 平台的运维核心能力。RuoYi 的 OTA 采用三级模型:

固件管理 → 升级任务 → 设备记录


六、数据模型解读

6.1 三库架构

RuoYi IoT 最让我眼前一亮的是它的三库架构设计:

存储引擎 存什么 为什么
MySQL 产品、设备、物模型、规则、告警、OTA 等元数据 关系型数据,需要事务和复杂查询
TDengine 设备上报的属性历史消息日志 时序数据,写入量巨大,需要高效的时间范围查询
Redis 设备最新属性值、设备在线状态、规则缓存 高频读取,低延迟要求

划重点!!! 这种"元数据存 MySQL、时序数据存 TDengine、热数据存 Redis"的三层存储策略,是 IoT 平台的最佳实践。如果你要自己做一个 IoT 平台,这个架构可以直接抄。

6.2 核心表关系

iot_product_category (产品分类)
    │
    └── 1:N ──> iot_product (产品) ──> 1:N ──> iot_thing_model (物模型)
                    │                                    │
                    │ 1:N                                 │ 物模型映射
                    v                                    v
              iot_device (设备) <── 1:N ── iot_device_modbus_point (Modbus 点位)
                    │                                    │
                    │ self-ref                            │ N:1
                    │ (gatewayId)                         v
                    └──> 网关-子设备拓扑        iot_device_modbus_config (Modbus 配置)

iot_scene_rule (场景联动) ──triggers──> 产品/设备/物模型
                    ──actions──> 产品/设备/告警配置

iot_data_rule (数据流转) ──source──> 产品/设备
                    ──sink──> iot_data_sink (数据目标)

iot_ota_firmware (固件) ──> iot_ota_task (任务) ──> iot_ota_task_record (记录) ──> iot_device

6.3 物模型 JSON 设计亮点

物模型(Thing Model)是整个 IoT 模块的灵魂。RuoYi 的物模型设计完全对标阿里云 Alink 规范,每条物模型记录代表一个"功能"(属性/服务/事件),通过 JSON 字段存储详细定义:

// 属性(Property)的 JSON 结构
{
  "identifier": "temperature",    // 标识符
  "name": "温度",                  // 名称
  "accessMode": "r",              // 读写模式: r(只读) / rw(读写)
  "required": true,               // 是否必需
  "dataType": "float",            // 数据类型
  "dataSpecs": {                  // 数据规格
    "min": -40,                   // 最小值
    "max": 120,                   // 最大值
    "step": 0.1,                  // 步长
    "unit": "°C"                  // 单位
  }
}

数据类型系统使用了 Jackson 多态反序列化(@JsonTypeInfo + @JsonSubTypes),支持 9 种数据类型:int、float、double、enum、bool、text、date、struct、array。每种类型对应不同的 DataSpecs 子类,这个设计非常优雅——用一个 JSON 字段就能表达复杂的类型定义。

6.4 多租户隔离策略

租户隔离的表(TenantBaseDO) 不隔离的表(BaseDO)
iot_product(产品) iot_product_category(产品分类)
iot_device(设备) iot_device_group(设备分组)
iot_scene_rule(场景规则) iot_thing_model(物模型)
iot_device_modbus_config iot_data_rule(数据规则)
iot_device_modbus_point iot_data_sink(数据目标)
iot_alert_config / iot_alert_record
iot_ota_firmware / iot_ota_task / iot_ota_task_record

设计思路: 产品、设备、规则是租户级别的数据,需要隔离。而物模型、告警配置、OTA 固件等是平台级别的数据,所有租户共享。这个划分是合理的——比如告警模板通常由平台统一定义,租户只需要配置自己的场景联动规则。


七、产品设计亮点与槽点

7.1 让我眼前一亮的设计

亮点 1:消息总线的渐进式架构

从 Local(单机)→ Redis Stream(轻量分布式)→ Kafka/RocketMQ/RabbitMQ(重度分布式),用户可以根据业务规模平滑升级,不需要改一行代码,只需要改一个配置项:

yudao:
  iot:
    message-bus:
      type: redis  # 改成 kafka 就完事了

这个设计降低了 IoT 平台的入门门槛——开发阶段用 Local,上线后切 Redis,设备量大了再切 Kafka。

亮点 2:EMQX 桥接模式

很多企业已经在使用 EMQX 作为 MQTT Broker,如果让他们"换"成 RuoYi 内置的 MQTT Server,迁移成本太高。RuoYi 的做法是:通过 HTTP Hook 对接 EMQX 的认证/鉴权/事件回调,同时作为 MQTT Client 连接到 EMQX 订阅设备消息。这样 EMQX 还是 EMQX,只是"大脑"换成了 RuoYi。

亮点 3:物模型驱动 TDengine 表结构自动同步

当产品的物模型修改后,调用 syncProductPropertyTable 就能自动同步 TDengine 的超级表结构——新增属性自动 ALTER TABLE ADD COLUMN,数据类型自动映射。这个"物模型即 Schema"的设计大大降低了运维成本。

亮点 4:Modbus 双模式支持

同时支持 Modbus TCP Client(主动轮询)和 Modbus TCP Server(被动响应),覆盖了工业 IoT 中两种最常见的 Modbus 部署模式。而且 Modbus 点位可以从物模型自动生成(updateDeviceModbusPointByThingModel),减少了手动配置的工作量。

亮点 5:设备认证完全对标阿里云 Alink

MQTT 认证采用 HMAC-SHA256 签名,Topic 格式 /sys/{productKey}/{deviceName}/...,方法命名 thing.property.post 等,全部遵循阿里云 Alink 规范。这意味着:已有的 Alink 兼容设备可以零改造接入 RuoYi IoT。

7.2 我觉得可以改进的地方

槽点 1:规则引擎能力偏弱

当前的场景联动规则是简单的 Trigger-Condition-Action 模型,不支持:

  • 多步骤串联(A 触发 → 延时 5 分钟 → 检查条件 → 执行动作)
  • 函数计算(对属性值做数学运算后再判断)
  • 可视化编排(拖拽式规则链)

ThingsBoard 的规则链引擎支持 50+ 种节点类型,包括脚本节点、延迟节点、消息过滤、关系判断等。如果要做复杂的 IoT 场景,RuoYi 当前的规则引擎会不够用。

改进建议: 可以引入类似 Node-RED 的可视化规则编排,或者集成 LiteFlow / Aviator 等轻量级规则引擎。

槽点 2:缺少设备影子(Device Shadow)

当设备离线时,如果云端想下发一个指令(比如"开灯"),当前系统只能等待设备上线后再发。阿里云 IoT 的"设备影子"功能可以缓存期望状态,设备上线后自动同步。这个功能在智能家居、智能楼宇等场景非常实用。

槽点 3:告警系统缺少分级升级机制

当前告警只有简单的"触发 → 记录 → 处理"流程,缺少:

  • 告警升级(INFO 告警 30 分钟未处理 → 升级为 WARN)
  • 告警收敛(同一设备 5 分钟内连续触发 → 合并为一条)
  • 告警静默(维护期间屏蔽特定告警)

这些是企业级告警系统的标配能力。

槽点 4:OTA 不支持差分升级

当前 OTA 是全量升级(每次下载完整固件),对于固件体积较大的设备(比如带 Linux 系统的智能摄像头),全量下载耗时耗流量。差分升级(只下载变化部分)是更高级但很实用的能力。

槽点 5:部分表缺少租户隔离

OTA 相关的三张表(固件、任务、记录)和告警配置/记录都没有做租户隔离。如果要做多租户 SaaS 平台,这些可能需要调整——比如不同租户应该有自己的固件版本和升级策略。


八、发散性思考

8.1 这个模块还能做什么?

读完代码,我脑子里冒出很多可以扩展的方向:

方向 1:边缘计算

当前的架构是"设备 → 网关 → 云端"的集中式处理。但在工业场景中,很多实时性要求高的计算(比如异常检测、PID 控制)不能依赖云端往返。如果能在网关层嵌入轻量级规则引擎(Edge Rules),让网关本身就能做简单的判断和响应,会大大提升实时性。

方向 2:数字孪生

物模型 + 时序数据 + 设备拓扑,这三样东西组合起来就是"数字孪生"的基础。如果再加一个 3D 可视化层(比如基于 Three.js),就能构建工厂/楼宇的数字孪生体。

方向 3:AI + IoT(AIoT)

设备上报的时序数据天然适合做异常检测和预测性维护。比如:

  • 用 LSTM 模型预测设备何时会故障
  • 用聚类算法发现异常设备
  • 用时间序列分析优化能耗

RuoYi 已经有 AI 模块(yudao-module-ai),如果把 AI 模块和 IoT 模块打通,就能实现"AIoT"的故事。

方向 4:低代码 IoT 应用开发

参考 ThingsBoard 的 Widget 系统,让用户通过拖拽组件的方式快速构建设备管理界面。想象一下:产品经理不需要写代码,就能拖出一个"温度监控仪表盘",这对企业用户的吸引力是巨大的。

8.2 如果让我重新设计

如果从零开始设计这个 IoT 模块,我会做以下调整:

  1. 规则引擎升级: 引入基于 DAG(有向无环图)的规则链引擎,支持可视化编排、条件分支、延时、函数计算。参考 ThingsBoard 的规则链设计,但用更轻量的实现(比如基于 LiteFlow)。

  2. 设备影子层: 在 Redis 中为每台设备维护一个 Shadow JSON(包含 reported 和 desired 两个状态),设备上线时自动同步差异。

  3. 插件化协议适配: 当前协议是硬编码在 gateway 模块中的。如果设计成 SPI 插件机制(IotProtocolProvider 接口 + @SPI 注解),第三方开发者就可以自己写协议适配器,不需要改核心代码。

  4. 数据湖集成: 除了 TDengine,增加对数据湖(如 Apache IoTDB、MinIO + Parquet)的支持,方便做离线分析和机器学习。

8.3 技术迁移思考

RuoYi IoT 的很多设计思路可以迁移到其他场景:

设计思路 可迁移场景
消息总线多实现 任何需要"渐进式架构"的系统(如微服务间通信)
物模型(Schema 驱动) API 网关的请求/响应 Schema 校验、低代码平台的组件属性定义
三库架构(MySQL + 时序 DB + Redis) 任何需要同时处理元数据和时序数据的系统(如监控系统、日志平台)
协议抽象层(IotProtocol 接口) 任何需要多协议适配的系统(如支付网关对接多种支付渠道)
场景联动规则引擎 营销自动化(用户行为触发 → 条件判断 → 发券/发短信)、DevOps 自动化运维

九、关键代码导读

最后,列出 5 个最值得深入阅读的代码文件,按优先级排序:

1. 消息总线核心:IotMessageBus.java

路径: yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/mq/messagebus/core/IotMessageBus.java

为什么值得读: 这是整个 IoT 模块的"血管系统"。网关和业务层之间的所有通信都通过这个接口。理解了这个接口的设计,就理解了 IoT 模块的架构精髓。它的 5 种实现(Local/Redis/Kafka/RocketMQ/RabbitMQ)也是学习消息中间件抽象的好材料。

2. 设备消息路由:IotDeviceMessageServiceImpl.java

路径: yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java

为什么值得读: 这是业务层的"消息路由器"——所有从设备端上报的消息都在这里被分类处理。handleUpstreamDeviceMessage 方法根据 method 字段将消息路由到不同的处理器(状态更新、属性存储、事件记录、OTA 进度、拓扑管理等)。读懂了这个文件,就读懂了 IoT 业务层的核心流程。

3. MQTT 协议实现:IotMqttProtocol.java

路径: yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/protocol/mqtt/IotMqttProtocol.java

为什么值得读: 这是 IoT 网关最核心的协议实现。基于 Vert.x 的 MqttServer,实现了完整的 MQTT 服务端——连接认证、Topic ACL、QoS 管理、上下行消息处理。如果你想学习如何用 Java 实现一个 MQTT Broker(或者至少理解 MQTT 协议的工作原理),这个文件是最好的参考。

4. 场景联动规则引擎:IotSceneRuleServiceImpl.java

路径: yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java

为什么值得读: 这是 IoT"智能"的核心——场景联动规则引擎。executeSceneRuleByDevice 方法展示了如何根据设备上报数据匹配规则、评估条件、执行动作。规则引擎的设计模式(Trigger → Condition → Action)可以迁移到很多其他场景(营销自动化、运维自动化等)。

5. 物模型与 TDengine 同步:IotDevicePropertyServiceImpl.java

路径: yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java

为什么值得读: 这个文件展示了"物模型驱动存储"的核心理念——defineDevicePropertyData 方法根据物模型定义动态创建 TDengine 超级表,saveDeviceProperty 方法将属性值同时写入 TDengine(历史)和 Redis(缓存)。这种"Schema 驱动存储"的设计思路非常值得学习。


总结

RuoYi-Vue-Pro 的 IoT 模块是我见过的开源 IoT 平台中功能最完整、架构最合理的实现之一。它有三个让我印象深刻的设计决策:

  1. 消息总线解耦网关和业务,支持从单机到分布式的平滑演进
  2. 三库架构(MySQL + TDengine + Redis),每种数据存到最适合的地方
  3. 物模型驱动一切——从 API 定义到存储 Schema 到 Modbus 点位映射,物模型是贯穿整个系统的核心抽象

如果你正在做一个 IoT 相关的项目,或者想学习 IoT 平台的架构设计,强烈建议把 yudao-module-iot 的代码 clone 下来,按照上面推荐的 5 个文件顺序阅读。相信我,读完之后你对 IoT 平台的理解会提升一个层次。

下一篇预告: IM 即时通讯模块——看看 RuoYi 是怎么在 Spring Boot 里实现一个完整的 IM 系统的,敬请期待!

觉得有用的话,点个赞支持一下呗~ 你们的点赞是我持续更新的动力!如果有任何疑问或者想讨论的话题,欢迎在评论区留言交流。

更多推荐