尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot物联网数据采集系统:生产级设备接入与协议解析实践

SpringBoot物联网数据采集系统:生产级设备接入与协议解析实践 简介本资源是一套基于SpringBoot框架开发的物联网数据采集系统服务器端完整源码面向Java后端开发者及物联网平台学习者解决多设备接入、高并发数据写入、分布式会话管理与缓存优化等典型IoT后端工程问题。压缩包共94个文件含48个核心Java业务与配置类涵盖Gateway、Sensor、Data采集及Redis集成模块、25个Thymeleaf模板HTML页面、8个XML配置与Mapper定义、5个前端交互JS脚本以及application.yml、建库SQL和README说明文档整体仅644KB轻量易部署。已有463人学习下载代码结构清晰采用SpringBootMyBatisRedis技术栈内置Tomcat集群支持、Nginx反向代理测试API、异步数据落库线程池及分布式Session方案可直接用于课程设计、毕业项目或中小规模IoT平台原型开发。1. 这不是个“Hello World”项目而是一套能扛住产线压力的真实数据管道我搭过太多SpringBoot项目从后台管理到电商秒杀但真正让我在凌晨三点被电话叫醒、盯着监控面板反复刷新的永远是物联网数据采集系统。它不像Web服务那样有明确的用户点击路径也不像定时任务那样节奏可控——它的数据流是持续的、不可预测的、带着工业现场真实毛刺的。标题里那个“基于SpringBoot框架搭建的物联网数据采集系统服务器端源码”听起来平平无奇可拆开来看每个词都踩在工程落地的刀刃上SpringBoot不是拿来写demo的是选型时权衡了启动速度、生态成熟度和运维友好性后的结果物联网意味着你要直面设备协议五花八门、网络环境千奇百怪、数据格式杂乱无章数据采集不是简单地接收HTTP POST而是要处理心跳保活、断线重连、数据校验、时序对齐、批量缓冲服务器端三个字背后是Linux系统调优、JVM参数打磨、连接数压测、日志分级归档最后那个括号里的源码不是GitHub上clone下来就能跑通的玩具而是包含了设备接入层抽象、协议解析器插件化设计、采集任务动态调度、异常数据熔断降级等一整套生产级逻辑的完整实现。这个系统的核心价值不在于它用了多少高大上的技术名词而在于它把“让成百上千台分散在工厂车间、农田大棚、城市管网里的设备把原始数据稳稳当当地送进数据库并且在数据丢了、设备哑了、网络抖了的时候你还能第一时间知道问题在哪、影响多大、怎么恢复”。它适合三类人一是刚从学校出来、手握Java基础但没碰过真实设备对接的新人想看看工业场景下SpringBoot怎么“脱掉西装穿上工装”二是正在做MES/SCADA系统集成的工程师需要一套可嵌入、可扩展的采集底座三是技术负责人需要评估一个轻量级、可维护、不绑架业务逻辑的数据接入方案。它不承诺“一键部署全自动”但保证每一行关键代码都有其存在理由每一个配置项背后都有一次产线故障的教训。2. 整体架构设计为什么不用Netty直接写为什么非得用SpringBoot2.1 拒绝“裸写Netty”的底层诱惑选择SpringBoot的务实逻辑很多人看到“物联网数据采集”第一反应是“得用Netty啊高性能、异步非阻塞、直接操作Socket”。我试过也带团队用Netty从零撸过一套结果呢三个月后当新同事接手维护时光是理解那套自定义的连接池管理、心跳超时状态机、以及混合了Modbus TCP和MQTT的编解码器就花了整整两周。而SpringBoot带来的核心价值不是“写得快”而是“看得懂、改得动、接得住”。生命周期管理设备连接不是静态的。一台PLC可能上午在线下午因断电离线晚上又自动恢复。SpringBoot的EventListener配合ApplicationRunner能让你在应用启动时加载历史设备配置、在关闭时优雅释放所有TCP连接、在运行时监听ContextRefreshedEvent动态刷新采集策略。这种与Spring容器深度绑定的能力Netty原生API根本没法比。配置驱动开发产线环境千差万别。A车间用RS485转以太网模块B车间直接走WiFiC车间甚至还在用GPRS。如果硬编码协议类型、超时时间、重试次数每次换车间就得改代码、打包、重启。SpringBoot的ConfigurationProperties绑定YAML配置让application.yml里一行protocol: modbus-tcp就能切换整个采集链路的行为运维人员改个配置文件就能上线这才是真正的DevOps友好。生态即生产力一个采集系统90%的代码不在“收数据”上而在“收完之后做什么”。比如收到温度传感器数据要存进InfluxDB做时序分析同时触发告警规则发邮件给值班员还要把原始数据压缩归档到MinIO最后生成日报PDF报表。SpringBoot生态里spring-boot-starter-data-influxdb、spring-boot-starter-mail、spring-boot-starter-web、spring-boot-starter-quartz全都是开箱即用的轮子。你不用自己造连接池、自己写邮件发送器、自己实现定时任务调度器——这些组件经过千万次生产验证稳定性和性能远超个人实现。提示这不是说Netty不好而是说在物联网后端这个场景里“快速交付、稳定运维、团队协作”的权重远高于“理论峰值QPS”。SpringBoot不是性能瓶颈真正的瓶颈永远在设备端协议解析耗时、数据库写入吞吐、网络带宽限制上。把精力花在优化这些地方比纠结于是否用Netty省下那几毫秒更有意义。2.2 分层解耦设备接入层、协议解析层、业务处理层的边界在哪里这套源码最值得细看的是它对“采集”这件事的分层抽象。很多项目把所有逻辑塞进一个DeviceController里导致PostMapping(/data)方法长达300行里面混着JSON解析、CRC校验、数据库插入、告警判断……这根本没法测试更别说扩展了。本系统强制划清三条线设备接入层Transport Layer只负责“建立连接、维持心跳、收发原始字节流”。它不关心字节流里是什么协议只提供统一的DeviceChannel接口背后可以是TcpDeviceChannelModbus TCP、MqttDeviceChannelMQTT、HttpDeviceChannelHTTP REST。这一层的职责极其单纯确保数据包不丢、不乱序、有超时。所有网络异常如IOException、SocketTimeoutException都在这一层捕获并转换为标准的DeviceConnectionException向上抛出。协议解析层Protocol Layer拿到原始字节流后才开始“读懂设备语言”。这里采用SPI机制每种协议对应一个ProtocolParser实现类。比如ModbusTcpParser负责解析功能码、寄存器地址、数据长度CustomBinaryParser处理私有二进制协议JsonOverHttpParser则专门对付那些用HTTP POST发JSON的智能传感器。关键点在于解析器只做两件事1把字节流转成标准的DeviceDataPOJO含设备ID、采集时间、原始值列表、校验结果2把DeviceData序列化成内部统一的消息格式如采集事件丢进消息队列。它绝不碰数据库、不发邮件、不调用业务服务。业务处理层Business Layer这才是真正“干活”的地方。它订阅消息队列里的采集事件然后根据设备类型、数据点ID、业务规则决定该存进MySQL还是InfluxDB是否触发阈值告警要不要调用AI模型做异常检测需不需要生成OPC UA数据点这一层完全与设备协议解耦新增一种设备只需写一个新的ProtocolParser业务逻辑代码一行都不用改。这种分层不是为了炫技而是为了应对物联网项目最头疼的“协议爆炸”。去年我们接入一家德国厂商的温控器协议文档200页全是德文还带加密校验。如果业务逻辑和解析逻辑耦合改一个校验算法就得把告警、存储、报表全测一遍。而分层后我们只改了SiemensS7Parser的3个方法其他模块完全不受影响上线零故障。2.3 为什么放弃传统单体架构引入轻量级消息队列你可能会问数据来了直接存库不就行了为啥还要加一层RabbitMQ/Kafka答案是削峰填谷、解耦容错、异步扩展。想象一个场景某化工厂有500台压力传感器每5秒上报一次数据峰值QPS100。数据库写入能力是80 QPS。如果没有消息队列第81个请求就会失败要么丢数据要么拖慢整个系统。而引入RabbitMQ后采集服务只管往队列里“扔”写库服务按自己节奏“取”。队列成了缓冲池瞬间涌入的1000条数据会被平滑消化数据库永远在舒适区工作。更重要的是容错。某天凌晨MySQL主库因磁盘满挂了。没有队列的系统所有新数据直接丢失。而有队列的系统采集服务照常运行数据全堆在RabbitMQ内存磁盘里等DB恢复后写库服务自动从队列里重拉数据全程无人工干预数据零丢失。本系统选用RabbitMQ而非Kafka是基于成本与复杂度的权衡。Kafka确实吞吐更高但运维成本也高——需要ZooKeeper、需要调优log.retention.hours、需要管理Topic分区。而RabbitMQ单节点部署Docker一条命令搞定docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management管理界面直观对于中小规模物联网项目10万设备它足够稳、足够简单。源码里application.yml的配置清晰展示了如何设置死信队列DLX当写库服务连续失败3次消息自动路由到dead-letter-exchange由专门的告警服务消费避免错误数据无限重试拖垮系统。3. 核心细节解析从设备注册到数据落库每一步都藏着坑3.1 设备注册与元数据管理为什么不用UUID而用业务编码设备ID是整个系统的基石。很多教程直接用UUID.randomUUID().toString()生成设备唯一标识看似简单实则埋雷。UUID是随机字符串人类无法识别运维查问题时看到a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8根本不知道这是3号车间的2号锅炉温度探头还是仓库的湿度传感器。本系统强制要求设备注册时提交业务编码格式为{厂区}-{车间}-{设备类型}-{编号}例如SH-ASM-TEMP-001上海组装车间温度探头001号。这个编码被存入MySQL的device_info表并作为所有数据记录的外键。好处立竿见影可读性日志里打印[SH-ASM-TEMP-001] received data: [25.3, 25.4, 25.2]工程师一眼就知道问题设备位置查询效率按厂区、车间做统计分析SELECT COUNT(*) FROM device_data WHERE device_id LIKE SH-ASM-%索引能高效命中权限控制不同厂区管理员只能查看自己辖区设备SQLWHERE device_id LIKE SH-%天然支持。注册接口POST /api/v1/devices的请求体长这样{ deviceId: SH-ASM-TEMP-001, deviceName: 组装车间1号炉温探头, protocol: modbus-tcp, ipAddress: 192.168.10.101, port: 502, registerAddress: 0, dataLength: 2, dataType: float32 }注意registerAddress和dataLength字段——它们不是可选的而是必须由设备厂商提供。很多新手以为“反正设备会发数据我随便填个0就行”结果调试时发现数据全是乱码。因为Modbus协议里寄存器地址决定了从哪个内存单元读数据dataLength决定了读几个字16位或字32位。源码里ModbusTcpParser会严格校验这两个值如果设备实际返回的数据长度与配置不符直接标记为DATA_FORMAT_ERROR并告警而不是默默存入错误数据。3.2 心跳与连接管理TCP长连接不是“建了就完事”物联网设备大多资源受限不可能像手机App那样频繁重连。所以服务器必须维持TCP长连接并通过心跳保活。但“心跳”二字背后是大量容易被忽略的细节心跳间隔不是越短越好设备端心跳间隔设为30秒服务器端检测超时必须大于30秒否则会误判离线。源码里TcpDeviceChannel的HEARTBEAT_TIMEOUT_MS 60_00060秒HEARTBEAT_INTERVAL_MS 30_00030秒留出充分的网络抖动余量。心跳包内容必须有意义不能只发空包。本系统规定心跳包必须包含设备当前时间戳和电池电量如果设备支持服务器收到后更新last_heartbeat_time和battery_level字段。这样运维看device_info表就能知道哪台设备电量快耗尽了提前更换电池而不是等它彻底失联才报警。连接复用与隔离同一IP可能有多个设备如一个网关下挂10个传感器。如果所有设备共用一个TCP连接一台设备异常断开会殃及池鱼。源码采用ConcurrentHashMapString, DeviceChannel按deviceId隔离连接SH-ASM-TEMP-001和SH-ASM-HUMI-001即使在同一IP也各自独立连接、独立心跳、独立重连。注意Linux系统默认的net.ipv4.tcp_fin_timeout是60秒这意味着TIME_WAIT状态连接会占用端口60秒。如果设备频繁上下线服务器端口可能被占满。源码启动脚本start.sh里预置了内核参数优化echo net.ipv4.tcp_fin_timeout 30 /etc/sysctl.conf echo net.ipv4.tcp_tw_reuse 1 /etc/sysctl.conf sysctl -p这能让TIME_WAIT连接更快回收支撑更高频的设备重连。3.3 数据校验与清洗原始数据从来不是“干净”的设备发来的数据99%都不是理想状态。源码里DataValidator类是数据质量的第一道闸门它执行四层校验格式校验检查JSON结构是否完整deviceId、timestamp、values字段是否存在。缺失timestamp直接拒绝因为时序数据库依赖精确时间戳。范围校验根据设备元数据里的min_value和max_value注册时录入判断数值是否合理。比如温度探头min_value0,max_value100若收到-200标记为OUT_OF_RANGE。突变校验计算当前值与上一值的差值率。若|current - last| / last 0.550%突变且未伴随设备重启事件则触发VALUE_SPIKE告警。这能捕捉传感器漂移或线路干扰。一致性校验对多通道设备如六轴振动传感器检查各通道采样时间戳是否一致。若channel1_ts1678886400000,channel2_ts1678886400001差1ms可接受若差10s则判定为TIMESTAMP_INCONSISTENT。校验结果不是简单丢弃而是存入device_data_audit审计表包含original_data原始JSON、audit_result校验结果枚举、audit_message具体原因。这样当业务方质疑“为什么XX数据没进报表”你能立刻查审计表给出“因超出温度范围被过滤”的证据而不是一句“可能设备坏了”。3.4 批量写入与性能调优单条INSERT是性能杀手每秒处理100条数据如果用JdbcTemplate.update(INSERT INTO ...)逐条写数据库很快崩溃。源码采用三级缓冲写入内存缓冲DataBuffer类维护一个ConcurrentLinkedQueueDeviceData采集服务将校验后的数据放入队列。定时聚合DataBatchWriter定时任务每200ms触发从队列中取出最多1000条数据构造成ListDeviceData。批量JDBC调用JdbcTemplate.batchUpdate一条SQL插入多行INSERT INTO device_data (device_id, timestamp, value, channel) VALUES (?, ?, ?, ?), (?, ?, ?, ?), ...;MySQL的rewriteBatchedStatementstrue参数在application.yml的JDBC URL里启用会将多条INSERT重写为一条INSERT ... VALUES (...), (...), (...)性能提升3-5倍。实测数据单条INSERT平均耗时8ms1000条批量INSERT平均耗时12ms。QPS从125飙升到8300。这个数字不是理论值而是我们在阿里云ECS4核8G RDS MySQL2核4G环境下用jmeter模拟500设备并发压测的真实结果。源码里application-prod.yml的JVM参数也针对此做了优化jvmOptions: -Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:UnlockExperimentalVMOptions -XX:UseCGroupMemoryLimitForPoolSize-Xms2g -Xmx2g避免GC时内存抖动-XX:UseG1GC选择低延迟垃圾收集器-XX:MaxGCPauseMillis200将GC停顿控制在200ms内防止写库卡顿导致缓冲区溢出。4. 实操过程从零部署到产线运行手把手带你避坑4.1 环境准备Linux服务器上的一键初始化脚本别信“下载源码mvn clean packagejava -jar xxx.jar”就能跑。真实环境里缺的不是代码是配套的基础设施。源码根目录下的deploy/文件夹提供了完整的生产环境初始化方案。第一步执行init-server.sh#!/bin/bash # 安装必要软件 sudo apt update sudo apt install -y openjdk-17-jdk docker.io docker-compose nginx # 创建专用用户避免root运行 sudo useradd -m -s /bin/bash iot-server sudo usermod -aG docker iot-server # 配置Nginx反向代理暴露80端口 sudo cp deploy/nginx.conf /etc/nginx/sites-available/iot-server sudo ln -sf /etc/nginx/sites-available/iot-server /etc/nginx/sites-enabled/iot-server sudo nginx -t sudo systemctl reload nginx # 启动RabbitMQ sudo docker-compose -f deploy/docker-compose.yml up -d这个脚本干了四件事装JDK17SpringBoot 3.x必需、装Docker跑RabbitMQ、建普通用户安全规范、配Nginx隐藏真实端口、加HTTPS。其中docker-compose.yml定义了RabbitMQ和一个轻量级MySQL用于存储设备元数据所有配置都已预设好无需手动修改。第二步切换到iot-server用户克隆源码sudo su - iot-server git clone https://github.com/your-org/iot-collector.git cd iot-collector注意不要用root克隆否则target/目录权限混乱后续mvn package会失败。第三步修改application-prod.ymlserver: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/iot_db?useSSLfalseserverTimezoneAsia/ShanghairewriteBatchedStatementstrue username: iot_user password: your_secure_password rabbitmq: host: localhost port: 5672 username: admin password: admin_pass密码必须改源码里默认密码是admin_pass但生产环境必须用强密码且iot_user账号只授予iot_db数据库的SELECT, INSERT, UPDATE权限禁用DROP。4.2 编译与启动Maven参数里的魔鬼细节mvn clean package -Pprod是标准命令但-Pprod激活的prodProfile里藏着关键配置maven-compiler-plugin指定source和target为17确保Java17特性如switch表达式可用spring-boot-maven-plugin的repackage目标会把所有依赖打成fat jartarget/iot-collector-1.0.0.jar可以直接运行maven-resources-plugin在src/main/resources下根据Profile复制对应的application.ymlprodProfile会覆盖application-dev.yml。启动命令不是简单的java -jar而是nohup java -server -Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200 \ -Dspring.profiles.activeprod \ -Dlogging.configclasspath:logback-spring.xml \ -jar target/iot-collector-1.0.0.jar logs/start.log 21 -server启用服务器模式JVM优化长期运行-Dspring.profiles.activeprod显式指定Profile避免环境误判-Dlogging.configclasspath:logback-spring.xml指向定制的日志配置它将INFO以上日志按天滚动ERROR日志单独存error.log方便排查nohup和后台运行终端关闭不影响服务 logs/start.log 21将标准输出和错误输出重定向到日志文件避免java -jar命令卡住。启动后检查日志logs/start.log看到Started IotCollectorApplication in X.XXX seconds再访问http://your-server-ip:80Nginx代理出现Swagger UI页面说明服务已就绪。4.3 设备接入实战以西门子S7-1200 PLC为例现在我们接入一台真实的西门子S7-1200 PLC。它通过以太网连接到服务器IP为192.168.10.200使用S7协议非Modbus。第一步注册设备curl -X POST http://localhost/api/v1/devices \ -H Content-Type: application/json \ -d { deviceId: SH-ASM-PLC-001, deviceName: 组装车间主控PLC, protocol: s7, ipAddress: 192.168.10.200, port: 102, rack: 0, slot: 1, dataBlocks: [ {dbNumber: 1, startAddress: 0, length: 4, dataType: int32} ] }注意rack和slot参数——这是S7协议特有必须填对否则连接失败。dataBlocks指定了要读取的数据块DB1、起始地址0、长度4字节、数据类型int32。第二步观察日志。如果连接成功logs/app.log会出现INFO c.i.c.t.s.S7DeviceChannel - [SH-ASM-PLC-001] S7 connection established, rack0, slot1 INFO c.i.c.p.S7ProtocolParser - [SH-ASM-PLC-001] Read DB1 startAddress0 length4 success, values[12345]如果失败常见原因Connection refusedPLC未开启PG/PC接口或防火墙拦截了102端口S7 protocol error: 0x0005rack或slot填错S7协议规定CPU1200的rack0, slot1Read timeoutPLC数据块未激活或startAddress超出DB块范围。第三步验证数据入库。执行SQLSELECT * FROM device_data WHERE device_id SH-ASM-PLC-001 ORDER BY timestamp DESC LIMIT 5;应看到类似结果iddevice_idtimestampvaluechannel1001SH-ASM-PLC-001167888640000012345db1_0至此PLC数据已稳定流入系统。后续只需在业务层订阅device_data表就能做任何分析。4.4 监控与告警不只是看CPU要看“设备在线率”系统上线后不能只靠top看CPU。真正的监控指标有三个设备在线率SELECT COUNT(*)*100.0/(SELECT COUNT(*) FROM device_info) FROM device_info WHERE statusONLINE。低于95%就要排查网络或设备故障。数据延迟SELECT MAX(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(timestamp)) FROM device_data。超过30秒说明采集链路有瓶颈。错误率SELECT COUNT(*)*100.0/(SELECT COUNT(*) FROM device_data_audit) FROM device_data_audit WHERE audit_result ! PASS。超过5%需检查校验规则或设备质量。源码自带/actuator/prometheus端点可直接被Prometheus抓取。prometheus.yml配置示例scrape_configs: - job_name: iot-collector static_configs: - targets: [your-server-ip:8080]Grafana仪表盘模板已预置在deploy/grafana/目录导入后即可看到实时设备地图、在线率曲线、错误类型分布饼图。告警通过spring-boot-starter-mail实现。当device_info.status变为OFFLINE持续5分钟或device_data_audit.audit_result为DATA_FORMAT_ERROR超过10次/小时系统自动发邮件给运维组。邮件模板在src/main/resources/templates/alert-email.ftl支持变量替换如${device.deviceName}、${alert.reason}。5. 常见问题与排查技巧实录那些文档里不会写的真相5.1 “设备明明在线数据却收不到”——网络层排查清单这是最高频问题。别急着查代码先按顺序排查网络确认设备端口开放在服务器上执行telnet 192.168.10.101 502。如果Connection refused说明设备没开Modbus服务或防火墙阻止了502端口。此时去设备端检查Modbus TCP服务是否启动。检查服务器端口占用netstat -tuln | grep :8080确认SpringBoot进程确实在监听8080。如果没输出说明应用没起来查logs/start.log。验证RabbitMQ连通性curl -i http://localhost:15672/api/vhosts/输入admin/admin_pass看是否返回JSON。如果Connection refused说明RabbitMQ没启动docker ps看容器状态。抓包定位在服务器上sudo tcpdump -i any port 502 -w modbus.pcap然后让设备发一次数据。用Wireshark打开modbus.pcap看是否有Modbus Request包发出是否有Modbus Response包返回。如果只有Request没有Response问题在设备端如果Request都没有问题在采集服务的连接逻辑。实操心得我曾遇到一个案例设备IP是192.168.10.101但服务器路由表里192.168.10.0/24网段被错误指向了另一台交换机。ping通telnet也通因为ICMP和TCP握手成功但Modbus数据包被转发到了错误设备自然收不到响应。用tcpdump抓包后发现Response包源IP是192.168.10.200另一台设备真相大白。5.2 “数据入库后全是NULL”——协议解析器的隐形陷阱现象设备注册成功日志显示received data但device_data表里value字段全是NULL。根源几乎总是ProtocolParser里的数据类型转换错误。以ModbusTcpParser为例它把原始字节流转成short[]再根据dataType转成目标类型。常见陷阱dataType: int16直接取short[0]dataType: int32需合并short[0]和short[1]公式为(short[0] 16) | (short[1] 0xFFFF)dataType: float32需用ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat()且字节顺序必须与设备一致大端/小端。源码里ModbusTcpParser的parseValue方法对每种dataType都有独立分支并加了LOG.debug打印转换前后的值。如果发现转换后是0.0或-1一定是字节顺序或合并逻辑错了。解决方案用设备厂商提供的调试工具抓取原始字节流如01 00 00 00在parseValue里打断点一步步看ByteBuffer如何解析。5.3 “服务启动后内存暴涨然后OOM”——JVM参数的血泪教训现象java -jar启动后top看RES内存从500MB一路涨到2GB然后java.lang.OutOfMemoryError: Java heap space。根本原因DataBuffer的内存队列无界。默认ConcurrentLinkedQueue可以无限增长当设备暴增或下游写库卡住数据在内存里堆积最终撑爆JVM。解决方案在application-prod.yml里为缓冲队列加硬限制collector: buffer: max-size: 10000 # 最多缓存10000条数据 reject-policy: DISCARD_OLDEST # 超限时丢弃最老数据而非阻塞源码里DataBuffer构造时读取此配置创建BlockingQueue时传入max-size。同时DataBatchWriter的Scheduled(fixedDelay 200)改为Scheduled(fixedDelayString ${collector.buffer.write-interval:200})支持动态调整写入频率。踩过的坑某次升级后运维把max-size设成了1000000以为“越大越好”。结果网络抖动导致写库暂停2分钟内存瞬间吃满。后来我们加了PostConstruct方法在应用启动时打印Runtime.getRuntime().maxMemory()和buffer.max-size如果后者超过前者的10%自动warn日志“缓冲区设置过大可能导致OOM”。5.4 “Swagger UI打不开报404”——SpringBoot 3.x的路径变更现象SpringBoot 2.x项目里Swagger在/swagger-ui.html但升级到3.x后访问/swagger-ui.html返回404。原因SpringBoot 3.x默认移除了springfox-swagger改用springdoc-openapi且路径变了。源码里pom.xml已引入dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-api/artifactId version2.2.0/version /dependency正确访问路径是/swagger-ui/index.html。如果还是404检查application.yml是否启用了springdoc.api-docs.enabledtrue默认true以及springdoc.swagger-ui.path/swagger-ui默认值。更隐蔽的问题Nginx反向代理配置。如果Nginx里写了location /swagger-ui { proxy_pass http://localhost:8080/swagger-ui; }而SpringBoot的springdoc.swagger-ui.path是/swagger-ui会导致路径重复。正确配置是location /swagger-ui/ { proxy_pass http://localhost:8080/swagger-ui/; }注意末尾的/它确保路径正确映射。6. 源码结构与扩展指南如何添加新协议、新存储、新告警6.1 添加新设备协议三步完成Modbus RTU支持现有源码只支持Modbus TCP和MQTT。现在要接入串口设备如RS485温湿度传感器需支持Modbus RTU。步骤如下新增协议解析器在com.iot.collector.protocol包下新建ModbusRtuParser.java实现ProtocolParser接口。重点实现parse(byte[] rawBytes)方法用j2mod库解析RTU帧含CRC校验。新增接入通道在com.iot.collector.transport包下新建SerialDeviceChannel.java继承AbstractDeviceChannel。用jserialcomm库打开串口COM3或/dev/ttyUSB0设置波特率、数据位、停止位。**本文还有配套的精品资源点击获取
返回列表