基于Spring Boot的校园网络运维平台:设备监控与工单管理实战解析
发布时间:2026/9/26 2:14:48来源:尧图网络
学校网络运维是个典型的看着不起眼、做起来一堆事的方向。设备分散在不同楼栋、网络故障往往等学生打电话才发现、设备台账靠Excel管理、工单流转全靠微信群喊话——这套系统的出发点就是把这些问题收拢到一个统一的后端服务里用Spring Boot做核心骨架接设备的监控数据采集、告警推送和工单全流程管理。这篇文章会从需求拆解、技术选型、核心模块实现到踩坑实录完整讲一遍适合正在做类似毕设、课设题目的同学参考也适合学校信息中心想用低成本方式自建运维平台的朋友。我尽量把代码、配置、设计思路讲得具体不整虚的。1. 项目背景与需求拆解学校网络运维到底难在哪1.1 校园网络环境的真实痛点先说需求因为如果不了解学校网络的实际情况做出来的系统很容易变成为了用Spring Boot而用Spring Boot的玩具。一个普通规模的学校高校校区或者较大的中小学网络设备大致是这样分布的核心机房几台核心交换机、出口路由器、防火墙数量不多但地位极高各楼栋弱电间每栋楼至少一到两台汇聚交换机教学楼、实验楼、宿舍楼是重灾区接入层设备AP无线接入点、接入交换机数量最大动辄几百台服务器区DHCP服务器、DNS、教务系统服务器、监控存储设备这些设备的问题是品牌型号杂华为、锐捷、H3C、思科甚至TP-Link都有、部署分散、没有统一的监控手段。运维老师日常的工作不是高科技排障而是接到报修电话后先判断故障范围然后跑楼栋、进弱电间、看指示灯、查网线。用一句话概括需求能不能把人去找故障变成系统推故障给人。1.2 用户角色和功能需求梳理做系统设计的第一步是列角色。这套网络运维系统里我划分了四种角色角色核心诉求典型操作学生/教职工网络出问题了能快速报修、知道处理进度提交报修工单、查看工单状态运维人员第一时间知道设备故障、有处理工单的工具查看告警、接收工单、上报处理结果系统管理员管理设备台账、配置监控规则设备的增删改查、监控阈值配置领导/统计分析人员了解网络整体运行情况、工作成果数据查看统计报表、故障分布图基于角色功能模块就清晰了设备管理核心是资产台账记录每台设备的IP、位置、型号、维保信息、SNMP凭证支持Excel批量导入状态监控定时探测设备在线状态采集关键接口的流量数据发现异常产生告警告警管理告警产生、确认、恢复的闭环通过WebSocket实时推送到运维端界面工单管理报修-派单-处理-完成-回访的状态流转覆盖从用户报修到运维处理的完整链路数据统计设备在线率、告警类型分布、工单处理时长等基础维度这些模块不复杂但组合起来就是一个能真正落地的运维工具。1.3 非功能性需求的现实考量除了功能非功能性需求往往是项目能否被实际使用的关键。这个系统我定了几个硬指标响应速度监控页面打开不能超过2秒否则运维老师没耐心用并发能力按5000人在线的学校计算报修和查询的QPS其实很低但监控轮询任务必须能扛住几百台设备的并发采集易部署打包成一个Jar包就能跑不要搞复杂的中间件依赖方便部署到学校已有的服务器上可视化必须有基础的面板最好有校园网络拓扑图这个做到什么程度看时间但设备统计页面是底线明确了这些才开始进入技术选型不然很容易选出一堆看起来高级但没必要的组件。2. 技术选型与架构设计为什么选这套组合2.1 Spring Boot为什么是核心选Spring Boot不是因为它新而是因为它适合这种业务逻辑清晰、需要快速交付、运维要求低的内部系统。第一Spring Boot的自动配置把这套系统绝大部分的基础设施工作做完了。内嵌Tomcat打成一个Jar扔到服务器上就能跑连部署文档都省了。我记得第一次部署的时候服务端的Java环境装好一条nohup java -jar network-ops.jar 就起来了。第二生态成熟。做Web接口用Spring MVC做定时任务用Scheduled做权限用Spring Security做数据访问用Spring Data JPA或者MyBatis-Plus全都是一站式方案不需要自己造轮子。第三学校信息中心这种场景后续大概率会有其他系统要对接比如统一身份认证CAS/OAuth2、短信网关、企业微信通知。Spring Boot在这方面的接入资料最多踩坑也最容易找到答案。版本上我强烈建议直接用Spring Boot 3.x。虽然2.x还在被很多老项目使用但3.x的Jakarta命名空间迁移、AOT编译、虚拟线程支持都是长期受益的东西。特别是JDK 21的虚拟线程对网络运维这种大量IO等待型任务的场景非常友好后面章节会单独说。2.2 数据库、缓存、消息推送的选型理由数据库MySQL 8.x。选它没有悬念学校机房基本都有MySQL环境数据量级别设备几百台、工单一年几千条也完全在MySQL的舒适区内。真正要注意的是表结构和索引设计后面会展开。缓存Caffeine本地缓存。没有选Redis是有意为之。这套系统的缓存热点是设备实时状态在线/离线/告警数据量几百条更新频率是分钟级用本地缓存完全够。引入Redis反而多了个中间件要维护单点问题还要考虑。当然如果学校本身已经有Redis集群可以顺带用那也合理选型没有绝对的对错要看部署环境。实时推送WebSocket。告警产生后要立即出现在运维人员的页面上HTTP轮询每隔几秒拉一次体验太差而且浪费资源。WebSocket是全双工通道服务端可以主动向浏览器推消息。Spring Boot对WebSocket的支持在spring-boot-starter-websocket包里一套下来改动量很小。另外热词里提到的Spring Boot集成WebSocket的yml配置这个确实没什么特别要配置的主要是注册Handler和拦截器官方文档花20分钟就能跑通。对象存储MinIO。用来存网络拓扑图、设备配置文件备份、导出的Excel报表。MinIO兼容S3协议部署是单机一个二进制在校园网内部跑一个实例非常轻量。SNMP采集SNMP4J。这是Java生态里最成熟的SNMP协议库没有之一。后面会详细讲。2.3 整体架构和项目结构架构上这套系统不搞微服务。一个单体Spring Boot应用按模块分包未来如果需求真的大到需要拆分大概率不会模块边界也已经留好了。network-ops-system/ ├── config/ # 配置类WebSocket、Security、缓存、异步任务 ├── controller/ # REST接口层 ├── service/ # 业务逻辑层 ├── mapper/ # MyBatis-Plus数据访问层 ├── entity/ # 数据库实体 ├── dto/ # 前端交互对象 ├── task/ # 定时任务监控轮询、状态计算 ├── snmp/ # SNMP协议采集封装 ├── websocket/ # WebSocket推送实现 └── utils/ # 工具类前端我用的方案是Vue3 Element Plus前后端分离。接口全部走RESTful风格返回统一的Result结构。如果不想引入Node构建链路用Thymeleaf模板引擎也可以但交互体验会差不少而且Vue生态的表格、表单组件确实省时间。考虑到这是面向内部用户的系统前端完成度直接影响使用意愿所以我选了Vue。3. 核心模块实现从设备管理到告警推送的完整拆解3.1 设备管理模块台账是运维的地基设备模块是整套系统的数据基础没有准确的台账监控告警、工单都无从谈起。数据库设计这块我放几个核心字段CREATE TABLE net_device ( id BIGINT AUTO_INCREMENT PRIMARY KEY, device_name VARCHAR(100) NOT NULL COMMENT 设备名称, device_type VARCHAR(20) NOT NULL COMMENT 类型:router/switch/ap/firewall/server, ip_address VARCHAR(45) NOT NULL COMMENT 管理IP, mac_address VARCHAR(20) DEFAULT NULL COMMENT MAC地址, location VARCHAR(200) DEFAULT NULL COMMENT 位置描述如:逸夫楼3层弱电间, model VARCHAR(100) DEFAULT NULL COMMENT 设备型号, snmp_version VARCHAR(5) DEFAULT v2c COMMENT SNMP版本, snmp_community VARCHAR(50) DEFAULT NULL COMMENT 读写团体字符串, status TINYINT DEFAULT 0 COMMENT 0-离线 1-在线 2-告警, purchase_date DATE DEFAULT NULL COMMENT 采购日期, warranty_expire DATE DEFAULT NULL COMMENT 保修截止日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_ip (ip_address), INDEX idx_type (device_type) ) COMMENT 网络设备台账表;这里有几个实战中总结的经验IP字段用VARCHAR(45)不要用INT。IPv4本身能用INT存但IPv6是128位的到时候扩展只能改表结构运维系统一旦上线改表是伤筋动骨的事。VARCHAR(45)是IPv6文本形式的最大长度直接一步到位。另外学校网络环境里可能存在多个管理网段IP加个唯一索引要谨慎因为设备可能复用IP比如换了设备没及时清理台账建议只做普通索引靠业务逻辑保证数据质量。设备类型字段一定要规范。我见过有人用中文存比如核心交换机、无线AP查询统计的时候全是坑。统一用枚举值存界面展示的时候再映射成中文。设备管理接口是比较标准的REST接口用MyBatis-Plus的IService做CRUD非常高效。批量导入这块值得说一下学校第一次上系统时台账数据通常散落在Excel里几百条记录手工录入不现实所以一定要提供一个导入接口。用EasyExcel读Excel逐行校验IP格式、必填项、重复性校验失败的行生成错误报告返回给前端用户改完再重新导入。这个流程做完系统落地阻力会小很多。3.2 网络监控与数据采集核心中的核心监控模块是这套系统的技术核心也是和普通管理系统拉开差距的地方。先说采集协议。网络设备支持的标准管理协议是SNMP简单网络管理协议。几乎所有主流设备都默认支持SNMP通过管理IP加团体字符串v2c或者用户名密码v3就能读取设备状态。SNMP4J的基本用法如下实际上每台设备一轮采集的核心代码就是这样public class SnmpService { private final Snmp snmp; public SnmpService() throws IOException { TransportMapping? transport new DefaultUdpTransportMapping(); this.snmp new SnmpImpl(transport); snmp.listen(); } public String getOidValue(String ip, String community, String oid) throws IOException { CommunityTarget target new CommunityTarget(); target.setCommunity(new OctetString(community)); target.setAddress(new UdpAddress(ip /161)); target.setVersion(SnmpConstants.version2c); target.setTimeout(3000); // 超时3秒 target.setRetries(1); PDU pdu new PDU(); pdu.add(new VariableBinding(new OID(oid))); pdu.setType(PDU.GET); ResponseEvent event snmp.send(pdu, target); if (event.getResponse() null) { throw new IOException(SNMP请求无响应设备可能不兼容或网络不通); } VariableBinding vb event.getResponse().get(0); return vb.getVariable().toString(); } }常用的几个OID必须记住监控项OID说明系统运行时间1.3.6.1.2.1.1.3.0设备启动以来的时间单位百分之一秒系统名称1.3.6.1.2.1.1.5.0设备的sysName接口数量1.3.6.1.2.1.2.1.0设备上接口总数接口状态1.3.6.1.2.1.2.2.1.8遍历ifTable1-up 2-down接口入流量1.3.6.1.2.1.2.2.1.10累计入字节数Counter32/Counter64接口出流量1.3.6.1.2.1.2.2.1.16累计出字节数CPU使用率1.3.6.1.4.1.9.9.109.1.1.1.1.5注意不同厂商MIB不同这是Cisco的内存使用率1.3.6.1.4.1.9.9.109.1.1.1.1.13同上厂商各异监控轮询的调度设计我这里踩过坑。刚开始图省事直接在Scheduled注解方法里写了个for循环依次去请求所有设备。设备少50台以内没问题一旦设备上百台单线程串行采集一轮可能要十几分钟完全失去实时性。正确做法是每台设备的探测任务并行执行。用Spring的ThreadPoolTaskScheduler配置一个专门的调度线程池配合Java 21的虚拟线程可以这样搞Configuration public class MonitorTaskConfig { Bean(monitorScheduler) public ThreadPoolTaskScheduler monitorScheduler() { ThreadPoolTaskScheduler scheduler new ThreadPoolTaskScheduler(); scheduler.setPoolSize(8); // 调度线程池大小 scheduler.setThreadNamePrefix(monitor-); scheduler.setWaitForTasksToCompleteOnShutdown(true); scheduler.setAwaitTerminationSeconds(30); return scheduler; } // 设备在线状态探测任务每台设备单独提交并行执行 Scheduled(fixedRate 60_000) // 每分钟触发一轮 public void startDeviceProbe() { ListDevice devices deviceMapper.selectList(null); devices.forEach(device - monitorScheduler().execute(() - probeDevice(device)) ); } }流量采集要特别注意Counter类型的回绕问题。接口流量是以累计字节数的Counter形式存储的需要两次采集做差值再除以时间间隔才是速率。但Counter达到最大值32位是4.29亿后回绕到0重新计数如果不处理就会算出负流量或者巨大的假值。正确处理方式是判断current previous时用(current - previous MAX_VALUE)计算。三种探测手段的优先级是这样的ICMP Ping最快但不可靠设备可能禁ping、SNMP sysUpTime靠谱但需要设备支持、端口连通性TCP连接管理端口比ICMP准但不通用。我的方案是以SNMP为主、ICMP兜底SNMP能通就是在线否则Ping一次排除网络问题两者都不通才判定离线。3.3 告警与WebSocket实时推送故障主动找人告警模块的逻辑其实不复杂难在两点一是不要告警风暴二是消息要实时到达。先定义告警类型。按学校场景优先级排序设备离线告警严重设备状态从在线变为离线优先级最高接口Down告警重要关键接口down比如上联口、出口链路流量异常告警一般接口流量超过阈值持续一段时间恢复通知信息以上告警对应的设备恢复在线或流量回落后自动发恢复消息告警风暴是运维系统最常见的翻车现场。比如一台交换机因为停电离线了底下几十台AP全部跟着探测失败如果每台都发一条告警运维老师的手机直接爆炸。解决方案是告警合并和告警抑制同一楼栋的接入设备如果发现上层汇聚交换机也离线接入设备的告警不单独发只发一条XX楼汇聚交换机离线导致该区域AP离线的聚合告警同一台设备在告警未恢复前不重复发同类型告警限制重复告警间隔告警恢复后再次离线才产生新告警WebSocket推送的代码框架Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(alertWebSocketHandler(), /ws/alert) .addInterceptors(new HttpSessionHandshakeInterceptor()); } }Handler的核心逻辑是会话管理和消息广播Component public class AlertWebSocketHandler extends TextWebSocketHandler { // 在线会话集合用线程安全的Set保存 private static final SetWebSocketSession SESSIONS new CopyOnWriteArraySet(); Override public void afterConnectionEstablished(WebSocketSession session) { SESSIONS.add(session); // 连接建立后立即推送当前未确认的告警 session.sendMessage(new TextMessage(jsonUtil.toJson(pendingAlerts()))); } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { SESSIONS.remove(session); } public void sendAlertToAll(AlertMessage message) { for (WebSocketSession session : SESSIONS) { if (session.isOpen()) { synchronized (session) { session.sendMessage(new TextMessage(jsonUtil.toJson(message))); } } } } }这里有两个实用注意事项WebSocket必须配心跳。校园网环境里NAT超时、路由器空闲回收连接WebSocket可能半天没消息就静默断了。建议客户端每30秒发一个Ping消息服务端收到后返回Pong或者服务端定期给所有会话发心跳消息。Spring的WebSocketHandler里实现PongMessage处理就行。sendMessage方法要加锁。一个连接同时被多个线程推送消息时会抛ConcurrentModificationException或者消息错乱。上面代码里用synchronized保底实测下来很稳。3.4 工单管理模块让运维工作从微信群到系统化工单模块是整个系统的人与流程部分价值不亚于监控。以前学校报修靠电话和微信用户问修好了吗得人工翻聊天记录统计工作量更是没谱。工单状态流转设计待派单 - 处理中 - 已完成 - 已关闭 - 已退回重新派单状态机放在Service层每个状态变化方法做前置校验。比如处理中状态下不允许重复派单已完成必须填写处理方案。Service public class WorkOrderService { // 派单只能对待派单状态的工单操作 public void dispatch(Long orderId, Long assigneeId) { WorkOrder order workOrderMapper.selectById(orderId); Assert.state(order.getStatus() WorkOrderStatus.PENDING_ASSIGN, 当前状态不允许派单); order.setAssigneeId(assigneeId); order.setStatus(WorkOrderStatus.PROCESSING); // 可以在这里决定是否短信通知处理人 workOrderMapper.updateById(order); } }报修入口是给普通用户用的做得要简单。小程序或移动端H5都可以这里我用了H5页面。核心字段就三样故障描述、故障地址楼栋-房间-具体位置、联系电话。用户越多操作步骤越少越好不要让他选设备类型那该是运维人员判断的事。工单自动属主有个实用的优化学生报修时如果填了楼栋信息可以自动匹配负责该楼栋的运维人员优先派给他。这需要在运维人员表里存一个负责区域字段实现起来很简单体验提升却很明显。还有一个值得做的点工单处理完成后给报修人的满意度评价留一个入口。这个评价数据可以作为运维人员的月度考核参考领导看统计报表的时候非常看重这个指标。工单数据统计也不难但要注意统计口径。比如平均处理时长应该从派单时间算到完成时间而不是从提交时间算因为等待派单的时间不完全是运维的责任。4. 关键配置与细节优化Spring Boot 3那些值得注意的地方4.1 Java 21虚拟线程的正确使用方式既然用了Spring Boot 3.2JDK 21的虚拟线程是必须体验的特性。对网络运维系统这种场景虚拟线程的价值体现得很明显SNMP采集、第三方HTTP调用、IO等待这些任务都是耗时不耗CPU传统线程池下线程数开大了内存扛不住开小了并发不够。打开虚拟线程的方式很简单spring: threads: virtual: enabled: true打开后Tomcat处理HTTP请求的工作线程、Async异步任务默认使用虚拟线程。但对定时轮询任务我是手动调配的Bean(virtualExecutor) public Executor virtualExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); }然后用这个Executor去执行设备探测任务。这样200台设备的并发探测不再是折磨人的线程池调优问题虚拟线程按需创建用栈即焚内存占用几乎可以忽略。要提醒一点虚拟线程不是银弹有坑的地方是synchronized。JDK 21里synchronized会锁定平台线程导致虚拟线程的轻量特性退化。如果你的代码里有synchronized块且是高并发场景要改成ReentrantLock。不过按这个系统的并发量压力不大不用太较真。4.2 Spring Security 6的配置迁移Spring Boot 3里Spring Security升级到6.x很多旧写法直接废弃。最典型的是WebSecurityConfigurerAdapter这个类被彻底移除了。现在的标准姿势是定义SecurityFilterChain的BeanConfiguration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf - csrf.disable()) // 前后端分离关闭CSRF .authorizeHttpRequests(auth - auth .requestMatchers(/api/auth/login, /ws/**, /api/device/status).permitAll() .requestMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().authenticated() ) .sessionManagement(session - session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) ) .formLogin(form - form.disable()) .httpBasic(basic - basic.disable()); return http.build(); } }改动最多的坑旧的and()链式API在6.x里被移除了改成lambda表达式的写法antMatchers变成了requestMatchers。网上搜到的很多老教程都是废弃写法编译能过但启动直接报错。我建议新项目直接从官方文档的示例抄起不要抄旧博客的代码。这个系统用的是基于Session的认证方式JWT那套对内部系统来说有点多余。登录成功后把用户信息放到Session里后续请求通过HttpSession获取当前用户即可。唯一要注意的是WebSocket握手阶段的Session和HTTP的Session不是同一个所以要靠HttpSessionHandshakeInterceptor把用户信息带过去否则WebSocket连接里无法识别是哪个用户。4.3 MinIO和Caffeine的整合细节MinIO存文件的核心点不是上传下载而是临时访问凭证。学校内部系统一般没有公网访问需求但MinIO默认的桶是私有的直接拼URL访问会403。解决方案是让后端生成一个PreSigned临时URLString url minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(objectName) .expiry(900) // 15分钟有效 .build() );这个URL直接给前端用用户点开就能下载不需要暴露MinIO的AccessKey。Caffeine缓存我主要用在两个地方设备状态快照和告警去重。设备状态快照是监控模块的核心——每次轮询把每台设备的最新状态、最新流量数据放到Caffeine里前端页面比如设备列表读取时先查缓存缓存没有再走数据库大幅减少数据库压力。Configuration public class CacheConfig { Bean public CacheString, DeviceStatusSnapshot deviceStatusCache() { return Caffeine.newBuilder() .expireAfterWrite(30, TimeUnit.SECONDS) .maximumSize(10_000) .recordStats() .build(); } }为什么用30秒过期因为监控轮询本身是60秒一轮缓存30秒既不会让用户看到太久前的数据又能扛住页面的高频刷新。recordStats()可以配合Actuator暴露缓存命中率指标调优的时候再打开看就行。5. 常见问题与排查技巧实测中踩过的坑和解决办法5.1 常见问题速查表把开发这套系统阶段遇到的高频问题整理成表格后续自己排查也方便。很多是网上搜不到现成答案的。问题现象可能原因解决思路SNMP采集超时但设备能Ping通设备的SNMP团体字符串不对或ACL限制了管理IP的SNMP访问先在设备上手动执行snmpwalk -v2c -c community ip验证排除协议层面的问题。学校设备经常改动配置后忘了同步SNMP权限采集到的接口流量忽大忽小Counter32回绕、或64位Counter没有正确拼接优先读取Counter64OID后缀.2结尾判断current previous时按回绕处理WebSocket连接反复断开中间设备空闲超时回收连接、Nginx proxy_read_timeout过短客户端每30秒发ping服务端定期广播心跳。如果走Nginx代理配置proxy_read_timeout 3600s告警堆积一上线就发几十条系统重启后状态为空首次轮询把所有设备都判定为状态变化首次轮询只记录状态不产生告警。从第二轮开始变化才告警。这个逻辑要写死非常重要定时任务重复执行多实例部署时每个实例都在跑轮询这个系统是单实例部署不需要分布式锁。如果将来要集群可以用ShedLock或者把轮询任务独立成一个服务页面加载设备列表慢设备数量大时直接全表查询列表接口必须分页加IP和类型索引。如果需要展示实时状态用异步接口单独拉取不阻塞主列表5.2 SNMP调试方法论不要盲试OIDSNMP开发最大的坑就是不同厂商设备返回的数据格式不一致。我调试华为和锐捷的核心交换机时同一个系统运行时间OID一个返回TimeTicks类型一个返回的是字符串。如果代码里统一用.toString()取变量值就会出现带格式的括号和空格然后在解析阶段报错。应对方案是分层处理第一步裸请求验证。写一个小工具类任意设备任意OID直接请求先看原始返回值第二步厂商兼容封装。在SnmpService里对返回类型做判断variable.getSyntax()返回2是INTEGER、65是Counter64、67是TimeTicks按类型走不同解析逻辑第三步针对未知设备做保守处理。如果解析失败不要抛异常让轮询线程崩掉而是记录错误并把该设备标记为采集异常等下次轮询再试。等保测评的时候这条逻辑很加分实际开发中我建议先拿一台真实的交换机设备反复测试把数据采集和解析调稳定了再批量接入其他设备。一上来就接100台设备但SNMP解析没做好排查问题的成本会是十倍的。5.3 工单模块的几条业务经验工单模块的代码实现不复杂但有几条业务层面的经验值得说说。第一报修表单别做太复杂。我见过某学校自研系统报修表单里让用户选择网络故障类型DNS解析失败/物理链路中断/认证失败等等学生根本分不清楚全是乱选的。最终我的方案就是描述文本加地址剩下交给运维判断。让专业的人做专业的事用户做的操作越少越好。第二工单编号要有可读性。GD20250601001这种格式运维人员和学生都能直接念出来沟通比数据库自增ID好用得多。生成规则用日期加当日序号就够不要引入分布式ID组件。第三工单流转要留痕。每次状态变更保存一条操作日志出了纠纷时有据可查。数据量不大新建一张order_log表就行。5.4 性能优化的几个务实手段这套系统的数据量决定了它不需要复杂的性能优化手段但有三个优化建议非常有用。第一个是数据库连接池调优。默认的HikariCP配置是maximum-pool-size10对这套系统的并发量几十个用户同时操作够用但如果监控轮询任务和WebSocket推送同时访问数据库连接池不够会导致任务等待。我没有扩大连接池而是让轮询任务缓存优先、不直接打数据库避免无谓的数据库IO。第二个是接口层的冗余设计。比如设备列表页用户希望同时看到设备的基础信息和实时状态。如果每个设备状态都实时去查一次SNMP接口会慢到无法接受。我的做法是列表接口只返回数据库台账信息页面加载后再通过WebSocket或者一个单独的/api/device/status/batch接口去拉状态快照。这个快慢分离的思路在运维监控系统里非常实用。第三个是给关键SQL都加上EXPLAIN检测。MyBatis-Plus虽然好用但生成的SQL有时不走索引。最典型的是工单列表按状态和时间排序如果不加复合索引数据量几千条之后查询慢得很明显。我在上线前逐条看了慢查询日志加了两三个复合索引效果立竿见影。我在实际开发这套系统时的整体感受是Spring Boot降低了起步的成本但这套系统的真正价值在于需求拆解的准确——把学校网络运维最痛的设备分散、故障被动、流程混乱这三个点用尽量简单的技术组合解决掉而不是堆砌新技术。最后分享一个小技巧监控页面的状态展示建议带上最近一次的探测时间和阈值配置入口。运维人员看到告警后想做的第一件事不是去工单系统而是确认这个告警是不是误报、当前数据是多少、阈值设置是否合理。我在告警卡片上直接把这三样信息都展示出来实际使用中运维老师反馈说这是最省事的设计。如果后续要继续扩展可以考虑往这几个方向走对接企业微信或者钉钉机器人做告警通知引入网络拓扑自动发现基于LLDP协议和SNMP的邻居关系以及把配置文件定期备份到MinIO并在设备故障时一键下发恢复配置。这些都是在一个稳定底座上顺理成章的增量架构上不需要做大改动。
网站建设高端定制企业官网