☰
基于Spring Cloud的环境污染物数据分析与预测平台实战解析
2026/10/2 14:42:08 网站建设 项目流程

简介:这份资源是一套基于Spring Cloud的环境污染物数据分析与预测平台毕设源码,采用Consul、Zuul、Config等微服务组件,整合Kafka、Redis、Flask与Keras,覆盖数据可视化、空气质量排行、PM2.5预测、污染物预警、历史数据导出及后台管理等功能,数据源来自上海市环境监测中心及和风天气、腾讯天气等开放接口。资源共2000个文件,以js、css等前端文件为主,搭配java、xml等后端工程文件,以及json、md说明文档,压缩包整体57.49MB,适合计算机相关专业学生用于毕业设计、课程设计或期末大作业,也适合希望学习微服务与数据预测项目架构的开发者参考。项目代码完整且经过验证,内部模块划分清晰,附带详细项目说明,既可直接部署体验,也可在此基础上扩展预测模型、优化预警策略或重构前后端,作为二次开发起点。资源已有225人浏览学习,文档中对服务端口、缓存策略与模型训练流程均有说明,能帮助有基础的学习者更快上手。

1. 环境污染物数据分析与预测平台:Spring Cloud 毕设到底在做什么

如果你正在选毕设方向,又不想做那种“登录注册 + CRUD”的管理系统,那“基于 Spring Cloud 的环境污染物数据分析与预测平台”是一个很合适的切入点。它表面上是微服务架构的展示,实际上是一条完整的数据链路:采集污染物数据、清洗入库、统计分析、训练预测模型、把预测结果通过接口暴露给前端大屏。答辩时你可以讲架构、讲算法、讲数据流,任何一个点都能展开,不会冷场。

这套平台的直接价值在于:它把 Spring Cloud 的注册发现、网关路由、熔断限流这些微服务概念,落到了一个有真实业务含义的场景里。空气质量指数、PM2.5、PM10、二氧化硫、二氧化氮、臭氧这些指标大家都熟悉,预测“明天会不会超标”比预测“用户会不会流失”更容易让评委听懂。适合 Java 方向、想往大数据分析或环保信息化方向靠的本科生。

下文我会按“架构拆分 → 数据链路 → 预测模型接入 → 踩坑 → 验证演示”的顺序,把整个平台的落地路径讲清楚。你不用照着某个现成源码抄,按这套设计自己搭,也能搭出一模一样的东西。

2. 拆解微服务架构:Spring Cloud Alibaba 全家桶的选型与模块边界

2.1 服务怎么拆:从单体到五个微服务

很多毕设项目的问题是“微服务”只是个幌子,拆了三四个服务,代码加起来还不如一个单体大。环境污染分析与预测平台要拆得合理,得先看业务边界。我建议按数据流拆成五个基础服务:

  • gateway-service:网关,负责统一入口、鉴权、路由转发。
  • auth-service:登录与用户管理,维护操作员账号,生成 JWT。
  • ># 单机模式启动 Nacos,默认端口 8848 sh startup.sh -m standalone # Windows 下用 startup.cmd -m standalone

    启动后,在application.yml里让各个服务注册进来:

    spring: application: name: analysis-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 namespace: public config: server-addr: 127.0.0.1:8848 file-extension: yaml group: DEFAULT_GROUP

    这里两个参数容易被忽略。namespace如果不写,默认进 public 空间;毕设不需要多环境隔离,保持 public 即可。file-extension写 yaml 后,Nacos 会自动加载analysis-service.yaml这个配置,数据源、Redis 地址都可以放到 Nacos 上管理,这样部署到实验室服务器时不用改 jar 包里的配置,只改 Nacos 控制台就行。

    依赖方面,给 common 或每个服务引入:

    <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>

    注意bootstrap.yml里要配置spring.cloud.nacos.config.server-addr和spring.application.name,因为 Spring Boot 2.4 之后,config的导入时机变了,很多新人把配置写在application.yml里,导致 Nacos 上的配置始终不生效。

    2.3 网关与熔断:Gateway + Sentinel 的限流规则怎么订

    网关我用的是 Spring Cloud Gateway,不建议用 Zuul,性能和维护性都差一截。网关层要做两件事:按路径转发到对应服务,给预测接口单独做限流。

    spring: cloud: gateway: routes: - id: analysis-route uri: lb://analysis-service predicates: - Path=/api/analysis/** filters: - StripPrefix=1 - id: prediction-route uri: lb://prediction-service predicates: - Path=/api/prediction/** filters: - StripPrefix=1

    lb://是让网关走负载均衡,后面接服务名而不是 IP,这样服务换了端口也不用改网关配置。StripPrefix=1的意思是把/api/analysis/trend转成/trend再发给 analysis-service,服务内部接口不需要带api前缀。

    预测接口是耗时操作,必须单独限流,不然大屏一刷新、多用户同时点“预测”,prediction-service 的线程池会被占满。在 Sentinel 控制台加一条规则:针对prediction-service的/api/prediction/forecast资源,QPS 阈值设为 20,超出后直接排队或快速失败。一般毕设演示只有几个人用,QPS 20 足够,调太低反而会让演示现场误触限流,显得系统不稳。

    Sentinel 的接入依赖是:

    <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>

    接入后,在application.yml里打开 Sentinel 控制台地址:

    spring: cloud: sentinel: transport: dashboard: 127.0.0.1:8080

    这里要说清楚:Sentinel 的规则默认是内存态,重启服务就没了。毕设答辩现场重启是常事,我建议把核心限流规则用@SentinelResource注解写在代码里,或者用 Nacos 做规则持久化,不要靠在控制台手动配。

    3. 污染物数据从哪来:数据采集、清洗与入库链路

    3.1 数据源选定:真实 API、公开数据集与模拟生成器

    毕设的环境数据有三个来源,按推荐程度排序:一是国家和省市开放平台的历史空气质量数据,CSV 格式,字段齐全,适合做离线分析和模型训练;二是爬取实时监测站点数据,但反爬和稳定性会消耗大量时间,不建议作为主要数据源;三是自己写数据生成器,模拟一天 24 小时的污染物浓度变化曲线。

    我做过一次后最推荐的组合是:用公开历史数据训练模型,用模拟生成器持续产生实时数据供平台演示。为什么?因为真实的实时接口不稳定,答辩当天如果拉不到数据,整个分析页面上全是空图,那时只能干瞪眼。模拟数据生成器可以让你控制数据形态,比如让 PM2.5 在早晚高峰升高、在午后降低,趋势符合常识,解释起来可信。

    模拟生成器用一个独立的 Python 脚本跑,往 MySQL 写数据,Spring Cloud 这边只管读。这样采集链路不会影响微服务本身:

    import random import time from datetime import datetime import pymysql def generate_pm25(base_hour): # 早晚高峰因子:7-9点 和 18-21点 相对高,午后低 if 7 <= base_hour <= 9: factor = random.uniform(1.2, 1.5) elif 18 <= base_hour <= 21: factor = random.uniform(1.3, 1.6) elif 12 <= base_hour <= 15: factor = random.uniform(0.6, 0.9) else: factor = random.uniform(0.9, 1.2) return round(35 * factor + random.uniform(-5, 5), 1) while True: now = datetime.now() pm25 = generate_pm25(now.hour) conn = pymysql.connect(host='127.0.0.1', user='root', password='123456', database='air_quality') with conn.cursor() as cursor: sql = "INSERT INTO pollutant_data(station_id, pm25, pm10, so2, no2, ozone, collect_time) VALUES (%s, %s, %s, %s, %s, %s, %s)" cursor.execute(sql, ('station_01', pm25, round(pm25 * 1.6, 1), round(random.uniform(5, 15), 1), round(random.uniform(10, 30), 1), round(random.uniform(30, 80), 1), now)) conn.commit() conn.close() time.sleep(3600)

    注意参数里有两个细节。base_hour用的是now.hour,采集一次后sleep(3600)等待一小时,这样每天只有 24 条数据,连续跑一周能积累 168 条,足够支撑折线图和基本趋势分析。station_id写死为station_01,如果你想做多站点对比,可以循环生成station_01到station_05五条记录,模拟五个国控站点;对应地,SQL 的collect_time要统一到整点,方便按时间对齐做分析。

    3.2 定时采集与清洗:如何让数据进得整齐

    用 Python 脚本生成数据没问题,但它只是往表里插数据,真正让数据“干净”的是清洗逻辑。比如传感器偶尔会返回 0 或负值,超过 500 的 PM2.5 也要怀疑是异常点。清洗逻辑放在 analysis-service 里,启动时跑一次全量清洗,然后每天凌晨跑一次增量清洗。

    -- 将异常值和明显越界的记录标记为不可信 UPDATE pollutant_data SET is_valid = 0 WHERE pm25 <= 0 OR pm25 > 1000 OR collect_time IS NULL OR station_id NOT IN ('station_01', 'station_02', 'station_03', 'station_04', 'station_05');

    更完整的清洗还有一步:填补缺失时间点。站点每隔一小时采集一次,但如果采集程序挂了两个小时,时间序列就出现了空缺。模型训练时最怕数据断档,预测出来的值会偏得离谱。填补公式建议取前后两小时的平均值,不要用全表平均值,因为污染物浓度和一天中的时段强相关,用全局均值会把早高峰的数据强行拉低。

    INSERT INTO pollutant_data(station_id, pm25, pm10, so2, no2, ozone, collect_time, is_valid) SELECT station_id, (SELECT AVG(t2.pm25) FROM pollutant_data t2 WHERE t2.collect_time BETWEEN DATE_SUB('2025-01-10 08:00:00', INTERVAL 1 HOUR) AND DATE_ADD('2025-01-10 08:00:00', INTERVAL 1 HOUR) AND t2.is_valid = 1), -- pm10、so2 等同理 '2025-01-10 08:00:00', 1 FROM pollutant_data WHERE collect_time = '2025-01-09 08:00:00' LIMIT 1;

    这个 SQL 的逻辑是:如果 2025-01-10 08:00 这条记录不存在(在你执行插入前它是空的),就找到前一天相同时段的数据作为模板,取前后两小时的有效均值填进去。参数INTERVAL 1 HOUR表示窗口大小是前后各 1 小时;窗口越大越平滑,但太大就会把早晚高峰的形状磨平,毕设数据量小,取 1 小时足矣。

    3.3 存储选型:MySQL 与时序数据的取舍

    环境数据本质上是时序数据,正经企业会选 InfluxDB、TDengine 或 OpenTSDB。但毕设要注意评分维度和维护成本。用 TDengine,答辩时要解释为什么不用 MySQL,需要额外准备对比数据;用 MySQL 反而简单直接,因为平台里的分析报表、用户管理、预测结果都是关系型数据,统一放一个 MySQL 实例最省事。

    表结构设计上,我列一个实际跑通的清单:

    表名关键字段用途
    pollutant_dataid, station_id, pm25, pm10, so2, no2, ozone, collect_time, is_valid原始污染物数据
    aqi_levelid, station_id, aqi, level, primary_pollutant, collect_time空气质量指数与等级
    prediction_resultid, station_id, target_time, pm25_pred, pm10_pred, model_version, create_time模型预测结果
    sys_userid, username, password, role登录用户

    aqi_level表的一个细节:AQI 是根据六项污染物浓度分指数计算出来的,每项都折算成 IAQI,取最大值。这个计算逻辑写成一个 SQL function 或 Java 工具类都行,但值域区间必须和官方标准一致,比如 PM2.5 的 24 小时均值在 0-35 对应 IAQI 0-50,在 35-75 对应 50-100。答辩时评委很可能抽查这个换算公式,写错了就等于暴露数据分析基本功不扎实。

    4. 预测模块落地:把 Python 预测模型融入 Spring Cloud 微服务体系

    4.1 预测算法怎么选:ARIMA 还是 LSTM

    环境污染物浓度预测最常见的两个算法:ARIMA 和 LSTM。ARIMA 适合单变量、数据量小、趋势平稳的场景;LSTM 适合多变量、长序列、非线性关系明显的场景。毕设数据量一般只有几百条到两三千条,ARIMA 更容易收敛,参数也更好解释,答辩证时你能说出“ARIMA(p,d,q) 的差分阶数 d=1,消除了日周期性趋势”这种话,比说“LSTM 很强大”更能体现理解深度。

    不过我更推荐做成“双模型对比”的效果:先用 ARIMA 做一个基准预测,再用 LSTM 做一个预测,最后把两条曲线同时展示在前端,配上 MAE 对比。理由很实际:如果只用 ARIMA,评委可能会问“为什么不用深度学习”;只用 LSTM,又可能被问“你数据量这么小,凭什么相信它的泛化能力”。两个都有,展示对比,既显得工作量充足,又让算法选型有依据。

    教一个不会出错的基线模型:用前 168 小时(7 天)的数据预测未来 24 小时,步长为 1。污染物浓度有明显的天周期性,7 天窗口能覆盖工作日和周末的差异,效果比拍脑袋指定的 72 或 120 要好。代码在 Python 侧训练并保存模型:

    import pandas as pd import numpy as np from statsmodels.tsa.arima.model import ARIMA import joblib df = pd.read_csv('pm25_history.csv', parse_dates=['collect_time']) df = df.set_index('collect_time').sort_index() # 按小时重采样,空缺值前向填充 series = df['pm25'].resample('1H').ffill().dropna() # 固定参数:p=2, d=1, q=2,针对日周期数据效果比较稳 model = ARIMA(series, order=(2, 1, 2)) model_fit = model.fit() # 预测未来 24 小时 forecast = model_fit.forecast(steps=24) print(forecast) joblib.dump(model_fit, 'arima_pm25.pkl')

    这里order=(2, 1, 2)不是随便拍的。先用plot_acf和plot_pacf看自相关图,拖尾选 AR 项、截尾选 MA 项;数据量不够分析时,取 1-2 阶不会过拟合。ffill()是前向填充,比dropna()强在保持时间连续,但前面章节提到清洗时会补缺失值,所以这里的前向填充只是双保险。

    4.2 模型服务化:Flask 或 FastAPI 暴露接口,Spring Cloud 怎么调

    模型训练好之后,Python 侧不能只活在脚本里,要变成一个可以被 Java 调用的 HTTP 服务。我用的是 FastAPI,比 Flask 轻,自带 Swagger 文档,答辩时打开http://localhost:5001/docs可以直接给评委看接口列表,体验很好。在这个环节,“Python 应用融入 Spring Cloud Alibaba 微服务体系”这件事是热词场景里最关键的一步——微服务体系不必全是 Java,Python 模型服务也能以独立服务身份注册进 Nacos。

    from fastapi import FastAPI import joblib import numpy as np app = FastAPI() model = joblib.load('arima_pm25.pkl') @app.post("/api/prediction/forecast") def forecast(station_id: str, hours: int = 24): pred = model.forecast(steps=hours) result = { "stationId": station_id, "model": "ARIMA", "values": [round(float(x), 2) for x in pred], "modelVersion": "v1.0" } return result

    这个接口的入参只有station_id和hours,出参是预测值数组。注意values要转成 list,np.float64 不能被 FastAPI 直接序列化,这是很多人会忘记的细节,不转的话接口直接报 500。modelVersion字段很有用,前端和 Java 侧都不用改代码,模型升级后只改这个版本号就能追溯结果是由哪个模型产生的。

    Java 侧用 Feign 调这个接口。prediction-service 里定义一个接口类:

    @FeignClient(name = "python-prediction-service", url = "http://127.0.0.1:5001") public interface PythonPredictionClient { @PostMapping("/api/prediction/forecast") PredictionResponse forecast(@RequestParam("stationId") String stationId, @RequestParam("hours") int hours); }

    两个参数要解释。name是 Feign 的客户端名,url直接指向 Python 服务的地址,这个写法的好处是不依赖服务发现,Python 服务哪怕不注册进 Nacos 也能跑通全链路;如果想做得更工程化,可以在 Python 侧加nacos-sdk-python注册到同一个 Nacos,Feign 的url去掉,改成name = "python-prediction-service"加负载均衡,但毕设里直接写 url 更省事。

    4.3 预测接口的参数设计与结果落库

    预测服务拿到 Python 返回的数组后,不要直接丢给前端,要先落库。落库的意义在于:前端大屏反复刷新时,不用每次都去调 Python 模型,读历史预测结果即可;答辩时你也可以展示“昨天预测的 PM2.5 和今天实际监测的 PM2.5”的对比图,证明模型有验证闭环。

    落库代码在 prediction-service 里:

    @Scheduled(cron = "0 30 1 * * ?") public void runDailyForecast() { List<String> stations = analysisClient.listAllStations(); for (String stationId : stations) { PredictionResponse resp = pythonPredictionClient.forecast(stationId, 24); for (int i = 0; i < resp.getValues().size(); i++) { PredictionResultEntity entity = new PredictionResultEntity(); entity.setStationId(stationId); // 从当天 02:00 开始,按小时递增 LocalDateTime targetTime = LocalDateTime.now() .withHour(2).withMinute(0).withSecond(0).plusHours(i); entity.setTargetTime(targetTime); entity.setPm25Pred(resp.getValues().get(i)); entity.setModelVersion(resp.getModelVersion()); predictionResultMapper.insert(entity); } } }

    cron = "0 30 1 * * ?"表示每天凌晨 1 点 30 分跑一次,选择这个时间点因为大部分环境监测站的数据在凌晨 1 点前完成前一天的汇总,预测明天的数据时,训练输入没有缺失。targetTime从当天凌晨 2 点开始递增,是因为预测模型输出的第 1 个小时通常代表下一个整点,如果从 0 点开始会把“刚过去的那一小时”也算进去,导致画图时曲线整体左移一小时。这个小细节,前端比对时最容易暴露。

    参数化和规范化的顺序也常出问题。模型训练时用 MinMaxScaler 归一化,预测结果反归一化后才是真实浓度值。我见过不少同学把归一化范围写反,预测结果全部接近 0,或者出现负数。正确做法是训练时保存 scaler 对象,预测时用同一个 scaler 的inverse_transform还原,不能拿训练脚本里的统计值手工硬算。

    5. 毕设过程中最容易翻车的五个坑:现象、原因、解决

    5.1 Nacos 注册不上,服务列表永远是空的

    现象:每个服务都能正常启动,没有报错,但是打开 Nacos 控制台的服务列表,只有nacos自己,业务服务一个都不在。排查时发现网络通、8848 端口也能访问,就是注册不上。

    原因:最常见的是版本冲突。Spring Cloud Alibaba 2021.0.5.0 对应 Nacos Client 2.2.x,但如果你通过传递依赖拿到了 Nacos Client 1.4.x,控制台的 2.x 服务和 1.x 客户端之间存在兼容问题,注册会静默失败。还有一种情况是服务里同时引入了spring-cloud-starter-alibaba-nacos-discovery和旧版的com.alibaba.nacos:nacos-client,被后者覆盖了版本。

    解决:在依赖管理中强制锁定nacos-client版本为 2.2.1 以上,或在 pom 里用dependencyManagement统一版本。启动时加 JVM 参数-Dnacos.log.path=./logs能看到 Nacos 客户端日志,注册失败的真正原因基本都写在里面,不要只盯着控制台看。

    5.2 Feign 调用 Python 服务总是超时

    现象:前端点击“生成预测”后转圈超过 30 秒,最后提示网关超时。后端日志显示 Feign 调用失败,Read timed out。

    原因:Feign 默认连接超时是 1 秒,读取超时也是 1 秒。Python 模型加载需要时间,第一次调用尤其慢,ARIMA 的forecast虽然只预测 24 个点,但模型文件加载、数据对齐、Python 解释器预热都发生在第一次请求里,1 秒根本不够。这不是代码 bug,而是默认参数不适合跨语言调用。

    解决:在 prediction-service 的配置里调大 Ribbon 或 Feign 的超时时间:

    ribbon: ReadTimeout: 30000 ConnectTimeout: 5000

    同时建议 Python 服务在启动时就加载模型文件,而不是每次请求都joblib.load。代码里把model = joblib.load('arima_pm25.pkl')放在模块顶层,就能让模型常驻内存。实测首次请求能从 8 秒降到 200 毫秒左右,这比调超时更能解决问题。

    5.3 预测的时间戳和实际监测对不上,画出来的曲线错位

    现象:前端展示“预测 vs 实际”对比图时,两条曲线形态很像,但波峰波谷整体偏移几小时,看起来像模型故意把预测往后挪了。

    原因:这是时间序列里最容易忽略的边界问题。采集数据的时间戳用的是collect_time,模型训练时按它排序,但预测结果的target_time如果生成逻辑和训练数据的对齐方式不一致,就会错位。比如数据是 1 点整采的,但代表的是 0 点到 1 点的均值,预测的“下一小时”应该从 2 点开始,而不是从 1 点开始。

    解决:统一约定时间戳语义。我在数据清洗阶段就把collect_time全部规范为“时段结束时间”,并把这个约定写在项目说明文档里。预测接口返回的targetTime同样按“时段结束时间”生成,前端展示时不再做任何偏移。加上一条校验逻辑:预测结果表里每小时的target_time必须连续,缺任何一个小时的记录直接报警,不要让脏数据流到前端。

    5.4 8G 内存的笔记本跑不动全套服务

    现象:启动 Nacos、Gateway、四个微服务、Python 模型服务、MySQL、Redis,电脑直接卡死,风扇狂转,内存占用 95% 以上。

    原因:默认 JVM 堆内存太大了。Spring Boot 应用默认最大堆是物理内存的 1/4,四个服务各吃 1.5-2G,加上 Nacos 的 2G,还没开始跑业务就已经超了。另外 Nacos 2.x 的默认 JVM 参数偏向服务器端,不调优在开发机上很难受。

    解决:给每个服务设置合理的 JVM 参数。开发环境下统一设置:

    java -jar analysis-service.jar -Xms256m -Xmx512m java -jar prediction-service.jar -Xms256m -Xmx512m java -jar gateway-service.jar -Xms256m -Xmx512m

    Nacos 启动前修改bin/startup.sh里的JAVA_OPT,把-Xms和-Xmx从 2g 改成 512m。MySQL 和 Redis 不要用 Docker Desktop 一锅端跑,Docker Desktop 本身占用 2G 内存,换成本地安装版。这套配置调完后,全套服务内存占用能压到 4G 左右。

    5.5 预测结果全是同一个值,曲线变成一条直线

    现象:ARIMA 预测出的 24 个值几乎一样,或者缓慢变化后迅速收敛到一个固定值,前端曲线像一条水平直线,完全看不出趋势。

    原因:数据预处理阶段出了问题。最常见的是训练数据没有差分,或者数据里有大量重复值。ARIMA 模型对平稳性敏感,如果数据本身存在上升趋势或强季节性,order=(2,1,2)的差分阶数 d=1 不够,需要把差分阶数调到 2。另一个原因是训练集过短,只有几十条数据,模型学到的是简单均值,自然输出一条直线。

    解决:用 ADF 检验看数据是否平稳,p 值大于 0.05 就继续差分。代码里加一段判断:

    from statsmodels.tsa.stattools import adfuller result = adfuller(series) print(f'p-value: {result[1]}') if result[1] > 0.05: series = series.diff().dropna()

    如果 diff 一次后仍不平稳,就把d改成 2 再试。这条经验也适用于 LSTM:很多人把未经归一化的数据直接丢进 LSTM,loss 死活不降,输出基本恒定。先做数据探索,再进模型,别让超参数调优变成玄学。

    6. 让评委相信预测有效:验证指标、压测与演示脚本

    6.1 用 MAE、RMSE 和 R² 量化模型结果

    答辩时最常被问的一句话是:“你怎么证明你的预测有效?”不能只给一张图说“看起来趋势差不多”,要用回归指标说话。我建议在预测结果落库前加一个验证脚本:取最近 7 天的数据,用前 6 天训练、预测第 7 天,和实际值对比算指标。

    from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score actual = np.array([35.2, 38.7, 42.1, 39.5, 31.8, 28.9, 26.4]) predicted = np.array([36.0, 37.9, 40.5, 41.2, 33.0, 29.5, 27.1]) mae = mean_absolute_error(actual, predicted) rmse = np.sqrt(mean_squared_error(actual, predicted)) r2 = r2_score(actual, predicted) print(f'MAE={mae:.2f} ug/m3, RMSE={rmse:.2f} ug/m3, R2={r2:.3f}')

    三个指标的解读方式要提前准备好:MAE 告诉你平均差多少微克/立方米,比如 MAE 是 2.3,就是平均每天的预测误差约 2.3 个单位,这个精度在空气质量预测里已经可接受;RMSE 比 MAE 大说明存在少数误差较大的点,可以进一步分析是不是某天突然出现沙尘或强逆温;R² 在 0.6 以上就可以解释为模型捕捉到了大部分变化趋势,低于 0.5 就要考虑数据量不足或特征太少。

    指标参考标准判定
    MAE5 ug/m³ 以下趋势可用
    RMSE比 MAE 高 1.5 倍以内误差分布正常
    R²0.6-0.9模型有效,视数据量而定

    6.2 一键启动脚本与演示路径设计

    答辩现场最怕的是“起服务”变成“排障现场”。我习惯写一个start-all.sh,按顺序启动基础设施和业务服务,每个服务启动后打印日志文件路径,方便快速定位问题。

    #!/bin/bash # 启动顺序:MySQL / Redis -> Nacos -> 网关 -> 业务服务 -> Python 服务 ./start-mysql-redis.sh sh /opt/nacos/bin/startup.sh -m standalone nohup java -jar gateway-service.jar > logs/gateway.log 2>&1 & nohup java -jar analysis-service.jar > logs/analysis.log 2>&1 & nohup java -jar prediction-service.jar > logs/prediction.log 2>&1 & uvicorn python_service:app --host 0.0.0.0 --port 5001 > logs/python.log 2>&1 & echo "all services started. check logs under ./logs"

    演示顺序建议这样设计:先打开网关接口,用 Swagger 或 Postman 调一次历史趋势接口,展示数据源;再打开预测接口,跑一次 24 小时预测,让评委看到真实调用过程,而不是只点前端按钮——按钮背后的网络请求才是你工作量最直观的证明;最后打开对比图页面,把“预测 vs 实际”的曲线和 MAE 数值放在同一屏。注意 Python 服务要最先启动,因为它是冷启动最慢的一环;现场如果赶时间,可以把 Python 服务放在 Java 服务之前两秒启动,预留模型热加载时间。

    最后一件事是定时任务的开关。答辩演示时不要真的让数据采集定时任务开着,不然后台每分钟写数据库,会把前端图表搞乱。演示前把@Scheduled注解临时注释掉,或者通过配置中心的开关控制,让数据只读、不新增,页面效果更稳定。

    我做这类项目吃了不少亏,最大的教训就是提前把“演示当天一定会出问题”当成默认假设,所有外部依赖都留本地兜底方案。数据、模型、服务全部在本机跑通后,再去美化前端。希望这篇笔记能帮你把 Spring Cloud 环境污染物数据分析与预测平台从架构到细节都落地,少走我走过的弯路,祝答辩顺利。

    本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询