1. 项目概述:基于若依框架的物联网平台开发实践
第一次接触若依(RuoYi)框架是在2018年一个智慧农业项目中,当时需要快速搭建一个具备设备管理能力的后台系统。这个基于Spring Boot的快速开发框架,以其完善的基础功能和模块化设计,让我在两周内就完成了传统开发需要一个月的工作量。如今,将若依框架与物联网技术结合,可以构建出功能强大且易于维护的物联网管理平台。
物联网平台的核心使命是解决设备连接、数据采集和业务处理的闭环问题。传统开发方式往往需要从零开始构建用户管理、权限控制等基础模块,而若依框架已经提供了这些开箱即用的功能。根据我的实测数据,使用若依作为基础可以节省约60%的基础功能开发时间,让团队更专注于物联网特有的业务逻辑实现。
这个平台特别适合以下场景:
- 中小型物联网项目需要快速落地验证
- 传统企业数字化改造中的设备联网需求
- 教学演示或技术预研阶段的原型搭建
- 已有若依系统需要扩展物联网能力
2. 技术架构设计
2.1 基础框架选型分析
若依框架目前有单体架构和微服务架构两个版本。对于大多数物联网场景,我推荐使用单体架构版本(基于Spring Boot),除非遇到以下情况才考虑微服务版本:
- 设备连接数超过5万+
- 需要跨地域部署
- 团队具备成熟的微服务运维能力
技术栈组合建议:
- 前端:Vue2/Vue3(根据团队技术储备选择)
- 后端:Spring Boot 2.7 + MyBatis-Plus
- 数据库:MySQL 8.0(或PostgreSQL)
- 消息中间件:EMQX 4.4+(社区版已足够)
- 缓存:Redis 6.x
注意:若依默认集成的是Redis单节点,在生产环境中建议至少配置哨兵模式。我在某智慧园区项目中就曾因单节点宕机导致设备状态丢失。
2.2 物联网核心模块设计
物联网平台需要扩展的核心模块包括:
设备管理模块
- 设备档案(型号、SN、位置等)
- 在线状态监控
- 固件版本管理
数据采集模块
- 遥测数据接收(MQTT/HTTP)
- 数据解析引擎
- 存储策略配置
规则引擎模块
- 阈值告警规则
- 联动控制策略
- 场景化规则模板
可视化模块
- 设备面板定制
- 数据趋势分析
- 地理信息展示
在我的某工厂设备监控项目中,通过若依代码生成器快速构建了设备管理模块的基础CRUD功能,节省了约40个工时。
3. 关键实现细节
3.1 MQTT集成方案
EMQX与若依的集成是物联网平台的核心。推荐以下配置方式:
// application.yml 配置示例 emqx: broker: tcp://127.0.0.1:1883 username: ruoyi_iot password: 加密后的密码 client-id: ruoyi-server-${random.uuid} topics: - device/+/status - device/+/telemetry消息处理建议采用Spring Event机制:
@Service public class MqttMessageListener { @EventListener public void handleMqttMessage(MqttMessageEvent event) { String topic = event.getTopic(); String payload = event.getPayload(); // 业务处理交给异步线程池 ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor"); executor.execute(() -> processMessage(topic, payload)); } private void processMessage(String topic, String payload) { // 实际业务逻辑 } }3.2 设备鉴权实现
物联网设备连接需要严格的安全控制。推荐采用三重鉴权机制:
- 设备级认证:每个设备分配唯一的clientId和凭证
- Topic权限控制:限制设备只能发布/订阅指定主题
- 数据签名验证:重要指令需携带签名
若依的权限体系可以扩展用于设备管理:
-- 设备权限表设计示例 CREATE TABLE `iot_device_permission` ( `device_id` varchar(64) NOT NULL COMMENT '设备ID', `publish_topics` varchar(1024) DEFAULT NULL COMMENT '可发布主题', `subscribe_topics` varchar(1024) DEFAULT NULL COMMENT '可订阅主题', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`device_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;3.3 数据存储优化
物联网数据具有高并发写入的特点,需要特殊处理:
冷热数据分离:
- 热数据(最近7天):MySQL
- 温数据(1年内):TimescaleDB
- 冷数据(1年以上):对象存储
分表策略:
// 按月分表示例 public class DeviceDataShardingStrategy implements TableShardingStrategy { @Override public String determineTableName(String logicTable, Object params) { LocalDateTime now = LocalDateTime.now(); return logicTable + "_" + now.getYear() + "_" + now.getMonthValue(); } }- 缓存策略:
- 设备最新状态:Redis String(设置TTL)
- 历史数据查询:Redis ZSET(按时间排序)
4. 典型问题解决方案
4.1 设备连接不稳定
现象:设备频繁掉线,重连间隔不规则
排查步骤:
- 检查EMQX的
listeners.tcp.default配置# EMQX控制台命令 emqx_ctl listeners show - 调整KeepAlive参数(建议60-300秒)
- 检查网络延迟和丢包率
解决方案:
# 修改EMQX配置 echo 'listeners.tcp.default.keepalive = 300' >> /etc/emqx/emqx.conf systemctl restart emqx4.2 消息堆积问题
现象:消费者处理速度跟不上消息生产速度
优化方案:
- 增加消费者线程池
@Configuration public class ThreadPoolConfig { @Bean("iotMessageExecutor") public Executor iotMessageExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2); executor.setMaxPoolSize(50); executor.setQueueCapacity(1000); executor.setThreadNamePrefix("iot-message-"); return executor; } }- 启用消息批量处理
@KafkaListener(topics = "device.telemetry", containerFactory = "batchFactory") public void handleBatchMessages(List<ConsumerRecord<?, ?>> records) { // 批量处理逻辑 }4.3 权限同步延迟
现象:设备权限变更后未及时生效
解决方案:
- 实现EMQX的HTTP认证钩子
@RestController @RequestMapping("/auth") public class EmqxAuthController { @PostMapping("/device") public ResponseEntity<?> authenticateDevice(@RequestBody AuthRequest request) { // 实时查询数据库验证 boolean valid = deviceService.validateDevice(request.getClientid(), request.getUsername(), request.getPassword()); return valid ? ResponseEntity.ok().build() : ResponseEntity.status(401).build(); } }- 配置EMQX使用外部认证
# etc/plugins/emqx_auth_http.conf auth.http.auth_req = http://127.0.0.1:8080/auth/device auth.http.super_req = http://127.0.0.1:8080/auth/superuser5. 性能优化实践
5.1 数据库连接池调优
若依默认使用HikariCP连接池,物联网场景建议调整:
# application.yml spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000实测数据:在500并发设备场景下,上述配置比默认设置减少约30%的连接等待时间
5.2 EMQX集群配置
当设备数超过单节点承载能力时,需要搭建EMQX集群:
- 准备3台服务器(推荐配置:4C8G)
- 设置节点名称
# 每台机器修改/etc/emqx/emqx.conf node.name = emqx@192.168.1.1- 配置集群发现策略
cluster.discovery = static cluster.static.seeds = emqx@192.168.1.1,emqx@192.168.1.2,emqx@192.168.1.35.3 前端性能优化
设备列表页面对性能要求较高,推荐采用以下方案:
- 虚拟滚动技术
<template> <el-table :data="tableData" style="width: 100%" height="calc(100vh - 180px)" row-key="id" :row-height="54" :total="total" @scroll="handleScroll"> <!-- 列定义 --> </el-table> </template>- WebSocket实时更新
created() { this.socket = new WebSocket('ws://your-domain.com/ws/device'); this.socket.onmessage = (event) => { const data = JSON.parse(event.data); this.updateDeviceStatus(data); }; }6. 安全加固措施
6.1 通信安全
- MQTT over TLS:
# EMQX配置 listeners.ssl.default { bind = "0.0.0.0:8883" max_connections = 102400 ssl_options { keyfile = "/etc/emqx/certs/key.pem" certfile = "/etc/emqx/certs/cert.pem" cacertfile = "/etc/emqx/certs/cacert.pem" } }- API接口签名:
public class ApiSignInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String sign = request.getHeader("X-Sign"); String timestamp = request.getHeader("X-Timestamp"); // 验证时间戳(防止重放攻击) if (System.currentTimeMillis() - Long.parseLong(timestamp) > 300000) { throw new RuntimeException("请求已过期"); } // 验证签名 String expectedSign = generateSign(request, timestamp); if (!expectedSign.equals(sign)) { throw new RuntimeException("签名验证失败"); } return true; } }6.2 数据安全
- 敏感数据加密:
public class DataEncryptor { private static final String AES_KEY = "your-256-bit-key"; public static String encrypt(String data) { // AES加密实现 } public static String decrypt(String encrypted) { // AES解密实现 } }- 数据库字段加密:
@TableName("iot_device") public class Device { @TableField(typeHandler = EncryptTypeHandler.class) private String secretKey; // getters & setters }7. 部署与运维
7.1 容器化部署
推荐使用Docker Compose部署整套系统:
version: '3' services: emqx: image: emqx:4.4 ports: - "1883:1883" - "8883:8883" - "8083:8083" - "8084:8084" volumes: - ./emqx_data:/opt/emqx/data mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: yourpassword volumes: - ./mysql_data:/var/lib/mysql redis: image: redis:6 command: redis-server --requirepass yourpassword volumes: - ./redis_data:/data ruoyi: build: . ports: - "8080:8080" depends_on: - mysql - redis - emqx7.2 监控方案
Prometheus + Grafana监控:
- EMQX指标采集:
# etc/plugins/emqx_prometheus.conf prometheus.interval = 15 prometheus.push_gateway_server = "http://prometheus:9091" - 若依应用监控:
<!-- pom.xml --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>
- EMQX指标采集:
日志收集:
<!-- logback-spring.xml --> <appender name="ELK" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>logstash:5044</destination> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender>
8. 扩展与二次开发
8.1 多租户支持
物联网SaaS平台需要多租户隔离:
数据库层面:
ALTER TABLE iot_device ADD tenant_id VARCHAR(32) NOT NULL; CREATE INDEX idx_tenant ON iot_device(tenant_id);MyBatis拦截器:
@Intercepts(@Signature(type= Executor.class, method="query", args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})) public class TenantInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 自动添加tenant_id条件 Object parameter = invocation.getArgs()[1]; if (parameter instanceof Map) { ((Map) parameter).put("tenantId", getCurrentTenant()); } return invocation.proceed(); } }8.2 边缘计算集成
通过与边缘网关配合实现边缘计算:
- 边缘规则引擎:
# 边缘网关规则脚本示例 def on_message(msg): if msg.topic.startswith("sensor/temperature"): temp = float(msg.payload) if temp > 30: publish("actuator/fan", "ON")- 云端同步机制:
public class EdgeSyncService { @Scheduled(fixedRate = 300000) public void syncEdgeRules() { List<Rule> cloudRules = ruleService.listActiveRules(); edgeDevices.forEach(device -> { sendRulesToEdge(device.getId(), cloudRules); }); } }在最近的一个智慧路灯项目中,我们采用这种架构将云端计算负载降低了约40%,同时显著提升了控制响应速度。