
简介这是一套面向物联网开发工程师与系统集成人员的全栈式监控平台源码基于Java主流技术栈SpringMVCSpringMyBatis构建解决工业设备监控、智能家居环境感知及多协议视频接入等典型物联网场景中的数据采集、远程控制与大屏可视化需求。资源包共2000个文件涵盖279个Java后端逻辑文件、451个JSP页面模板、494个JS交互脚本、310个CSS样式文件及7个SQL建表脚本等完整支撑组态界面、MQTT/TCP/HTTP协议通信、海康摄像头视频流集成、报警管理、权限分级与历史数据报表等功能模块压缩包大小为832.96MB。已有236人学习下载配套齐全的PDF文档、配置说明与结构化目录便于二次开发与运维部署开箱即用显著降低从零搭建物联网平台的时间与技术门槛。 做物联网平台这件事圈内人都知道难的不是把页面写出来而是怎么把一堆五花八门的设备、协议、组态页面和大屏需求揉进同一个系统里。我手上这套基于Java全栈的物联网平台源码就是冲着这个痛点去的。它不是一个只跑通Demo的玩具而是把组态物联网、可视化大屏、MQTT/TCP协议接入、海康摄像头联动这几块硬骨头都啃下来的完整工程。本文会把平台的整体设计、核心模块实现思路、以及我在实际部署和二次开发中踩过的坑一次性讲透给准备做智慧工厂、智慧园区、设备监控类项目的朋友一个能直接参考的底子。先说结论如果你准备从零搭一套物联网平台最耗时间的不是CRUD而是设备接入层和数据可视化的联动。这套源码最大的价值在于它把“设备-平台-页面”这条完整链路打通了尤其是组态模块和大屏模块不是各玩各的而是共用一套实时数据总线。1. 整体设计与技术选型思路1.1 Java全栈技术栈怎么拆平台后端基于Spring Boot这是目前Java生态里做物联网平台最稳的起点。Spring Boot本身不解决设备接入问题但它的生态能让你快速把REST API、定时任务、权限管理、数据持久化这些基础能力搭起来把精力集中在协议解析和设备管理上。我的选型是这么拆的后端主框架Spring Boot 2.7.x MyBatis-Plus负责业务接口和系统管理实时通信层Netty处理TCP私有协议接入Eclipse Paho或Spring Integration MQTT处理MQTT消息消息中间件EMQX作为MQTT Broker也承担设备消息的吞吐缓冲数据存储MySQL存设备元数据、组态配置、历史报警Redis存设备最新状态和会话信息前端Vue 3 Element Plus负责后台管理页面ECharts渲染大屏图表组态编辑器用Canvas自研视频接入海康SDKHCNetSDK或RTSP拉流通过流媒体服务转成Web端可播放的格式这套组合不是拍脑袋选的。物联网平台有个特点设备量上来之后消息吞吐和状态查询会变成瓶颈。Redis存实时状态MySQL只做持久化落库两者分开查询性能才能扛住。Netty负责TCP这类长连接协议天然适合海量连接场景比用Tomcat线程池硬怼要靠谱得多。1.2 为什么主协议选MQTT而不是全走自定义TCP很多从传统工控转过来的朋友会问我以前的设备都是走自定义TCP报文为什么平台要主推MQTT我的答案是MQTT在广域网、弱网环境下的表现比裸TCP好太多。TCP自定义协议理论上灵活但你要自己解决连接保活、断线重连、消息确认、QoS服务质量这些基础问题而且不同设备的报文格式千奇百怪每接一种设备就要写一套解析。MQTT把这些问题全封装好了Broker帮你管理连接状态Topic帮你做消息路由QoS帮你保证消息不丢。对于设备接入层MQTT和TCP的关系不是二选一而是分层新接入的智能设备优先走MQTT开发效率高生态完善存量工控设备、PLC、DTU等走TCP私有协议通过Netty做适配层这个“双协议栈”的设计在实际项目里非常管用因为它兼顾了新旧设备的接入需求。尤其是做智慧工厂改造现场可能一半是老旧设备一半是新增智能终端平台必须同时吃得下。1.3 平台分层架构怎么理解整个平台按功能拆成四个逻辑层理解了这个分层后面看源码就不会迷路设备接入层负责协议解析、设备鉴权、心跳维护。这一层对上层屏蔽设备差异无论是MQTT设备还是TCP设备接入后都统一成平台内部的设备模型。数据处理层对设备上报的原始数据进行清洗、转换、存储。比如温度传感器上报的原始值是INT型根据设备配置的倍率转换成实际温度值再写入Redis和MySQL。业务服务层提供设备管理、告警规则、组态管理、大屏配置等业务接口。这层就是标准的Spring Boot应用。可视化展示层组态页面和大屏页面通过WebSocket或MQTT订阅实时数据驱动图表和图元刷新。这个分层的核心好处是“接入”和“展示”解耦。设备接入层加新协议不影响上层的组态和大屏大屏想换展示方式也不需要动设备接入层。2. 组态物联网模块的核心设计与实操要点2.1 组态的本质是什么组态这个词搞工控的人不陌生本质就是“用拖拽的方式把设备、管道、阀门等元素画成图再绑定设备数据实现实时监控”。传统组态软件都是C/S架构比如力控、组态王、MCGS。这套源码做的是B/S架构的组态纯浏览器运行不用装客户端这是现在做Web物联网平台的基本要求。组态模块拆开看核心是两个端组态设计器编辑态画布、图元库、属性面板、数据绑定配置组态运行时运行态加载配置好的JSON渲染画面订阅实时数据并驱动图元变化这两个端共用一个渲染引擎只是设计器在渲染引擎上叠加了编辑能力拖拽、选中、缩放、属性修改运行时则去掉了这些编辑交互纯展示。2.2 组态编辑器的技术方案编辑器这块我用的Canvas实现而不是SVG或者DOM拖拽原因有三个图元数量多的时候Canvas整体渲染性能更好尤其是涉及动画效果时Canvas对位图、复杂图形的绘制能力更强可以自定义各种工业图元导出成JSON配置时Canvas的坐标系和图形属性描述起来更直接编辑器核心要实现的交互图元拖拽从图元库拖到画布监听鼠标事件图元选中与移动点击选中拖动改变位置方向键微调缩放与旋转通过控制点实现图元缩放、旋转连线支持线段、折线、管道样式连线用于连接设备和流程图形绘制不复杂真正复杂的是数据绑定。每个图元上有一个“数据配置”属性将图元的某个视觉属性比如液位高度、背景色、文字内容、状态闪烁绑定到设备的某个测点如温度、开关状态。保存后生成的结构大致是{ id: element_001, type: tank, x: 120, y: 80, width: 120, height: 200, bindings: [ { attribute: fillLevel, deviceKey: device_001, pointKey: liquid_level, transform: value/100 }, { attribute: statusColor, deviceKey: device_001, pointKey: pump_status, mapping: { 0: #808080, 1: #00FF00 } } ] }运行时拿到这个JSON根据deviceKey和pointKey去订阅实时数据配合transform和mapping规则把原始值映射成视觉效果。理解了这个数据结构就理解了组态系统的灵魂。2.3 组态运行时的实时数据驱动组态运行时最怕的是数据刷新慢画面卡顿。我这里的做法是组态页面通过WebSocket连到平台的消息推送服务推送服务从Redis订阅设备状态变更每个设备在Redis里有独立的Key保存最新值设备上报后后端把最新状态推送到对应页面的WebSocket连接前端收到消息后根据消息中的设备标识和测点标识找到需要更新的图元做局部刷新这里有个关键优化组态页面只订阅当前画面中绑定到的设备而不是订阅全部设备。如果页面绑定了20台设备就只推送这20台的数据避免无效消息占满带宽。实测下来这个方案在50个图元、20台设备同时刷新时浏览器帧率稳定在50帧以上不会有明显的卡顿。2.4 组态编辑器的取舍经验自研组态编辑器是个耗时活我建议在动手前先想清楚边界如果项目主要是水电气、环保、车间设备这类偏标准化的场景自研图元库可以覆盖80%需求剩下20%做成“自定义图元”让用户上传图片或SVG来兜底不要一开始就追求Visio级别的编辑体验复杂编辑功能自动对齐线、组合、图层、撤销重做栈会消耗大量时间首批交付先把拖拽、绑定、运行这三件事做扎实我在做编辑器的时候撤销重做功能踩了不少坑。如果自己写历史栈要注意“绑定操作”也要纳入撤销范围否则用户改错绑定关系之后撤销不了体验很差。如果项目周期紧可以考虑引入开源的Canvas编辑器方案做二次封装省下底层工作量。3. 大屏可视化模块的搭建与数据联动3.1 大屏是一张什么“屏”大屏可视化是物联网项目里客户最喜欢看的部分也是最能体现项目效果的部分。大屏的本质是一张超宽分辨率的网页一般部署在拼接屏、LED屏或86寸以上的电视上常见分辨率为1920x1080、3840x1080甚至更高。大屏展示的内容通常分两层实时数据设备当前状态、最新采集值、在线率、报警数量统计分析趋势图、对比图、排行、累计值通常按小时/天/月聚合这两层数据来源不同实时数据走WebSocket推送统计数据分析走REST接口查数据库或查询聚合表。如果想把实时数据和历史趋势做到同一张大屏上前端需要分别处理这两条数据链路。3.2 大屏适配方案踩过的坑大屏最烦的问题就是分辨率适配。很多前端工程师用PC端的固定宽度写法结果到了大屏上要么左右留白要么上下溢出。我推荐的方案是rem vw/vh组合适配核心思路设计稿按1920x1080出根元素字体大小动态计算document.documentElement.style.fontSize (clientWidth / 1920) * 100 px所有尺寸用rem或百分比表示字体、间距、图表尺寸都跟着走这个方案在纯HTML/CSS布局下效果很好。但对于ECharts这类基于Canvas的图表库图表内文字大小和坐标轴标签需要手动响应式调整可以在图表初始化时根据当前设备的缩放比例动态设置文字大小。我遇到过另一个坑大屏浏览器长时间运行后内存涨得很快。原因是图表定时刷新时旧实例没有销毁。一定要在每次刷新前调用chart.dispose()或setOption(option, true)进行notMerge更新不能每次都重新new一个Chart实例。3.3 大屏数据动态刷新机制大屏的数据刷新策略直接决定展示效果这里我强烈建议实时类数据用WebSocket推统计类数据用定时器轮询或消息触发拉取。具体实现页面加载时先调一次REST接口拉取历史统计数据和设备列表快照建立WebSocket连接服务端根据大屏页面的订阅需求推送实时变化的数据统计图表趋势、柱状、饼状每5分钟或10分钟拉取一次聚合数据不用太频繁实时数据面板当前值、状态灯、在线数毫秒级刷新这个机制唯一的注意点是WebSocket推送的消息要带上时间戳前端要根据时间戳判断消息的时效性避免因为消息乱序导致页面显示旧数据。3.4 大屏组件选型建议大屏的图表选型目前基本是ECharts一家独大原因很简单开源免费、文档丰富、图表类型全。如果做的是3D场景、园区建模类的大屏可以考虑加Three.js或者Babylon.js做底座配合ECharts做二维图表这样视觉冲击力更强但开发和调试成本会高不少。我做这类大屏时图表配色会统一走一套设计Token避免每个图表开发时各配各的颜色导致大屏风格混乱主色调深蓝 #0A1628深色背景让数据更突出强调色青色 #00D4FF用于高亮和实时数据警示色橙色 #FF7D00用于告警和异常状态辅助色灰白 #A9B7C6用于坐标轴文字和次要信息大屏还有一个常被忽略的细节字体。大屏一般都离人远字体太细太小看不清。建议展示类数字用DIN或Bebas这类粗壮的字体正文用14px-16px核心数据字号至少28px以上。4. 通讯协议集成MQTT与TCP接入实战4.1 MQTT协议的核心机制MQTT是基于发布/订阅模式的轻量级消息协议物联网场景里几乎是事实标准。理解MQTT只需要抓住几个点Broker消息服务器所有消息都通过它转发EMQX是常用的开源BrokerTopic消息主题设备发布消息到某个Topic平台订阅对应Topic接收消息QoS消息服务质量0/1/2三档分别对应最多一次、至少一次、恰好一次遗嘱消息设备异常掉线时Broker发布预设的遗嘱消息通知其他客户端平台接入MQTT设备时最关键的是Topic设计。我用的是一套带层级关系的Topic规范方便按设备维度和业务维度做消息过滤d2p/{productKey}/{deviceKey}/data 设备上报数据 d2p/{productKey}/{deviceKey}/status 设备状态变更 p2d/{productKey}/{deviceKey}/control 平台下发指令 p2d/{productKey}/{deviceKey}/config 平台下发配置这样设计的优势是平台可以用通配符订阅d2p///data接收所有设备数据也可以精确订阅某个设备还能用d2p/{productKey}//data按产品维度过滤。设备侧只需关心自己对应的那三个Topic逻辑简单。4.2 Spring Boot整合MQTT的正确姿势很多初学者整合MQTT时都会在客户端ID、断线重连这些地方踩坑。我的做法是用Spring Boot Spring Integration MQTT可以自动管理连接和订阅比直接用Paho Client裸写稳定得多。核心配置大致如下mqtt: broker-url: tcp://localhost:1883 client-id: platform-server-001 username: admin password: admin123 default-topic: d2p///data qos: 1Configuration public class MqttConfig { Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory new DefaultMqttPahoClientFactory(); MqttConnectOptions options new MqttConnectOptions(); options.setServerURIs(new String[]{tcp://localhost:1883}); options.setUserName(admin); options.setPassword(admin123.toCharArray()); options.setAutomaticReconnect(true); options.setCleanSession(false); factory.setConnectionOptions(options); return factory; } Bean public MessageProducer mqttInbound() { MqttPahoMessageDrivenChannelAdapter adapter new MqttPahoMessageDrivenChannelAdapter(platform-server-001, mqttClientFactory(), d2p///data); adapter.setCompletionTimeout(5000); adapter.setQos(1); adapter.setOutputChannel(mqttInputChannel()); return adapter; } }这里要特别提醒两个关键参数都是实测踩坑经验setCleanSession(false)关闭清理会话。如果设为true客户端断开后Broker会清掉它的订阅和离线消息平台一重启就会错过设备在断线期间上报的数据客户端ID必须全局唯一如果有两个服务用同一个Client ID连同一个Broker前者会被踢下线造成连环掉线热词里提到的“启动服务出现死循环”那个问题多半是因为订阅了$sys/brokers//clients//connected这类系统主题然后在回调里又执行了重连或发布消息触发了新的连接事件形成死循环。解决方法是把“客户端上下线事件监听”和“消息处理逻辑”分开不要在事件回调里做消息发布或重连操作。4.3 TCP自定义协议接入存量设备走TCP时最核心的难点是处理半包和粘包。TCP是流式协议没有消息边界设备发来的一帧数据可能半路断开也可能几帧粘在一起。Netty里用Decoder解决我惯用的套路是固定长度报文用FixedLengthFrameDecoder简单粗暴分隔符报文用DelimiterBasedFrameDecoder适合以\r\n、FF等结尾的协议自定义头最常用报头里带长度字段用LengthFieldBasedFrameDecoder举个例子很多DTU的协议格式是帧头2字节 设备ID4字节 数据长度2字节 数据体 CRC校验2字节 帧尾2字节。用Netty的LengthFieldBasedFrameDecoder可以直接按长度字段拆包// 第5到6字节是数据长度字段整个帧最大1024字节头长度4数据长度偏移量5长度字段2字节长度调整量1初始剥离4字节 new LengthFieldBasedFrameDecoder(1024, 5, 2, 1, 4)拆包之后的核心工作是协议解析器把字节数组转成内部统一的数据模型再交给数据处理器。这里的经验是协议解析器要做成独立的策略类每种设备类型对应一个解析器通过工厂模式按设备型号动态选择这样新增协议不需要改动主流程。4.4 海康摄像头接入和Web端播放视频接入这块海康摄像头最常用的是两种方式RTSP拉流直接用摄像头RTSP地址拉取视频流海康SDK通过HCNetSDK做设备注册、取流、云台控制、对讲RTSP地址格式一般是rtsp://admin:passwordip:554/Streaming/Channels/101。但这个地址浏览器不能直接播放需要推流服务器转发常见的方案是用FFmpeg把RTSP流转成HLS或RTMP流Web端用video.js播HLS或通过flv.js播放HTTP-FLV流用ZLMediaKit这类流媒体服务自带WebRTC/HTTP-FLV/HLS输出我实测下来延迟要求不高的场景大屏监控墙、安防展示用HLS足够延迟3-5秒可接受如果做双向对讲的低延迟场景比如远程巡检用WebRTC才能把延迟压到500ms以内。平台里摄像头接入通常要走这么几步在后台添加设备录入IP、端口、用户名、密码、通道数平台调用海康SDK或RTSP探测验证设备连通性通过SSH或API配置流媒体服务拉流并转封装Web端拿到推流地址后播放最容易踩坑的是海康老设备和某些非标设备RTSP路径可能不一样通道号也有0和1两种写法。建议在设备接入前先用VLC本地拉一次RTSP确认路径是否真实有效再配置到平台上能省下大量联调时间。5. 常见问题与排查技巧实录5.1 高频问题速查表现象可能原因解决方案MQTT设备一直上线掉线客户端ID冲突多个连接共用同一ID检查客户端ID是否全局唯一必要时在ID后加随机后缀平台重启后收不到设备离线期间的数据cleanSession设为了trueBroker清掉了离线消息改为false并设置sessionExpiryInterval设备上报数据经常丢QoS配置为0且设备网络不稳平台订阅和设备发布都改为QoS 1组态页面图元不刷新WebSocket连接断开或数据绑定配置错误检查前端WebSocket状态后端确认消息是否推送到对应设备TCP设备出现乱码解码器拆包错误字节长度计算不对抓包确认报文格式调整LengthFieldBasedFrameDecoder参数大屏内存越来越涨图表实例未销毁或未复用使用chart.setOption(option, true) 或先dispose再重新init视频画面延迟大用了HLS且没有启用低延迟配置换WebRTC或HTTP-FLV切片时长调短海康摄像头长时间运行画面卡死码流过大或RTSP连接数超限限制单路码流增加流媒体服务并发数定期回收空闲拉流连接5.2 我在客户端ID和Topic通配符上踩的坑先说客户端ID。当时我写了一个模拟设备脚本用固定前缀加ID结果两个脚本同时跑后启动那个瞬间把前面那个踢下线设备状态在在线/离线之间疯狂抖动。排查了半天才反应过来是客户端ID重复。后面我养成了习惯客户端ID一律用设备编号加随机串拼起来保证同一个设备重连后生成的是新ID也不影响业务关联。再说Topic通配符。MQTT的通配符有单层和#多层两种平台订阅d2p///data时只匹配一层如果设备上报的Topic是d2p/productA/device_001/data刚好对上但如果某个设备走的是d2p/productA/device_001/extra/data这个订阅就收不到。我一开始订阅写成了d2p/#结果把所有层级的消息全收进来了数据量一大系统直接卡死。后面改成精确写d2p///data再加一个d2p///control_reply处理指令应答彻底解决消息泛滥的问题。5.3 组态JSON过于庞大时的加载优化组态页面的配置如果画了上百个图元JSON可能达到几百KB甚至1MB以上页面加载时会明显卡顿。我的优化思路是编辑器保存时做图层级拆分基础静态背景和动态图元分开存基础背景只在加载时渲染一次图元库的SVG图元尽量用简单的path避免加载大体积PNG数据绑定信息单独存不跟着渲染JSON一起加载运行时按需拉取对JSON做一次Gzip压缩输出加载后前端解压再用这个优化做完一张包含200个图元的组态页面加载时间从7秒降到了1.8秒体感效果非常明显。5.4 大屏和组态数据不同步的问题大屏和组态如果走的是不同的推送通道可能会出现两边数据不一致。比如组态页面走WebSocket推送大屏走MQTT订阅两边处理消息的延迟不一样用户会觉得很奇怪。我的做法是统一走后端消息统一推送服务后端从MQTT或Netty收到设备数据后统一写入Redis并发布一个内存事件由推送服务统一推给WebSocket客户端。这样不管是组态还是大屏都从同一个事件源拿数据数据一致性就有了保障。这个改动虽然简单但解决了大屏和组态“各说各话”的大问题。写在最后的实操体会这套平台源码做下来我最深的一个感受是物联网平台的核心不是功能多而是“链路通”。设备接入、协议解析、数据存储、实时推送、组态渲染、大屏展示每一环单独拎出来都不难难的是让它们在一个系统里顺畅协作。如果你打算基于这套源码做二次开发我的建议是先跑通一条最简单的链路用MQTT模拟一个温度传感器上报数据在组态页面绑定这个温度点再在大屏上把温度值和趋势图展示出来。这条链路跑通了整个平台的骨架你就摸清楚了后面再加设备、加协议、加页面都是往骨架上填肉。另外一个经验是协议接入一定要预留好扩展点。设备接入层的消息解析和数据处理做成策略模式新设备接入只是加一个解析类和一个配置而不是到处改主流程。这样平台才能从接入10台设备平滑演进到接入1000台设备。我实测中发现物联网项目交付后客户超过六成的时间在看大屏和组态画面真正去翻设备列表和告警记录的反而少。所以大屏和组态的交互体验、数据实时性、视觉观感值得投入最多精力去打磨。这也是这套平台把重头戏放在这两块的原因。后续如果你想扩展可以把精力花在设备告警策略引擎、设备OTA升级、多租户隔离这几个方向上这是平台从“能用”走向“好用”的关键。本文还有配套的精品资源点击获取