简介:基于Hadoop与Spring Boot的电力生产数据分析系统,是一份面向高校计算机科学与技术、人工智能、通信工程、自动化、电子信息等专业学生、教师和企业开发者的毕设级源码资源。项目围绕电力生产场景,整合HDFS存储、Yarn任务调度、PySpark数据预处理、Spring Boot后端与Vue前端,持久层采用MyBatis和Druid连接池,形成从大数据存储、分析到可视化展示的完整链路,亦可作为大数据技术栈的综合练手项目。压缩包共369个文件、约9.6MB,其中包含54个Java后端源码、24个Vue页面组件、13个Python数据处理脚本、SQL建表脚本、XML配置、项目截图与文档说明等,目录结构清晰,便于按模块查阅和二次开发。目前已有166人学习/下载,代码经过测试运行成功,作者答辩平均分达96分,适合毕业设计、课程设计或入门进阶时参考,下载后可依据README与搭建指引快速部署运行。
1. 电力生产数据分析系统:Hadoop和SpringBoot这对组合到底怎么分工
电力生产数据分析系统并不是一个新概念,但大多数课程设计和毕业设计项目都栽在同一个地方:用SpringBoot直连MySQL写一堆SQL,再画几张折线图,就管自己叫“大数据”项目。技术评审老师一眼就能看出你只在入门层面打转。而用Hadoop+SpringBoot组合,生成的电量趋势、负荷峰值、设备运行时长分析,既能接住海量历史生产数据,又能通过SpringBoot以接口形式对外输出,整体链路才算闭合。如果你正在找一个能落地、能演示、能写进简历的高分课题,这类系统原型是目前最稳妥的选型之一,它覆盖存储、计算、接口、可视化四个完整环节。
这篇文章里,我会把电力生产数据分析系统的完整实现路径拆开讲:Hadoop在里头存什么、算哪些指标、为什么需要跟SpringBoot配合,以及关键的五个以上翻车点。文中的所有配置和代码都基于Hadoop 2.x/3.x伪分布式或小型集群环境,SpringBoot统一用2.x版本。跟着走,你能在两三天内跑通一个可演示的完整项目。
2. 电力生产数据的存储与分析:先搞清楚Hadoop扛哪部分活
2.1 电力生产数据源长什么样,为什么非Hadoop不可
电力生产数据主要来自两类设备:一类是电站侧的传感器和采集终端,输出有功功率、无功功率、电压、电流、温度、压力等测点数据,频率从秒级到分钟级不等;另一类是营销和调度系统导出的台账数据,比如机组信息、电厂档案、停机记录、发电计划。前者是典型的时间序列流式数据,一天下来一个中型风电场就能产生几十万条记录,一年就是上亿条。把这种量级的数据直接灌进MySQL,做聚合统计时索引会膨胀得很厉害,一条按天的聚合SQL跑几十秒甚至几分钟都很正常。
Hadoop解决的问题是“先把海量原始数据低成本存下来,再用批量计算把统计结果算好”。比如你想统计过去一年某电厂每个月的发电量,传统做法是建一张按月分区的汇总表,但原始数据一多,ETL过程就会拖垮业务库。在Hadoop里,原始数据以文件形式落在HDFS上,用MapReduce或Spark作业按日跑批,聚合结果才写入MySQL。这样MySQL里永远只放小体量的结果数据,查询性能稳定,架构上也拉开了层次。
常见的做法是HDFS上按日期分区存放原始采集文件,目录结构形如/power_data/origin/2025/06/01/。这个做法有两个现实好处:一是文件追加和归档都很自然,按日期目录做增量导入即可;二是后续MapReduce任务可以只扫描需要的日期范围,避免全表扫描。
提示:不要把Hadoop当作高速查询引擎用。它的强项是吞吐量而不是响应速度,实时性要求高的场景请把任务交给SpringBoot+MySQL,Hadoop负责的是离线批量分析。
2.2 分析指标设计:哪些指标“看得见、算得出、讲得清”
电力生产数据分析系统的指标设计,直接决定你的项目评价高度。只看发电量太单薄,我一般会按三层来设计,覆盖从宏观到微观的完整链条。
第一层是生产总量指标:电厂日发电量、月发电量、年累计发电量,同比环比增长率。这些指标用于展示Hadoop对大批量历史数据的汇总能力,也是演示时最先展示的看板模块。
第二层是运行质量指标:设备平均负荷率、峰值负荷出现时段、机组启停次数、非计划停机时长、温度/压力越限次数。其中负荷率分析是一个很好的加分项:按小时聚合计算某电厂一天的负荷曲线,再用MapReduce找出最高值和出现时间,能证明你确实理解了业务而不只是会调接口。
第三层是能耗与效率指标:厂用电率、单位发电煤耗、线路损耗率。这些指标涉及多表关联计算,比如从发电量倒推煤耗,在MapReduce里通过二次排序或MultipleInputs实现,能体现一定的工程复杂度。
指标确定后,存储模型也一并定下来。HDFS存储原始明细数据,MySQL里建三张结果表:station_daily_summary(电厂日汇总)、unit_hourly_load(机组小时负荷)、station_monthly_trend(电厂月度趋势)。SpringBoot只查询这三张表,不碰HDFS上的原始文件。
2.3 Hadoop集群的最小可用配置:伪分布式还是真集群
伪分布式还是真集群,取决于你的机器资源和答辩场景。16GB内存的笔记本跑三个Docker容器组成的小集群,虽然能演示,但内存吃紧,I/O也不稳定,现场演示时容易卡顿。我的建议是:如果只是单人开发演示,伪分布式足够;如果希望体现集群部署能力,至少用三台虚拟机或云主机,每台4核8GB起步。
伪分布式的最小配置,核心是修改core-site.xml、hdfs-site.xml、yarn-site.xml三个文件。
core-site.xml设置NameNode地址和临时目录:
<configuration> <!-- NameNode 的 RPC 通信地址,9000 是 Hadoop 默认端口 --> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <!-- 临时目录必须配置,否则默认指向 /tmp,重启会被系统清理 --> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/data/tmp</value> </property> </configuration>hdfs-site.xml设置副本数和NameNode的HTTP访问端口:
<configuration> <!-- 伪分布式只有一台机器,副本数必须设为 1,否则 DataNode 上报副本不足会一直报错 --> <property> <name>dfs.replication</name> <value>1</value> </property> <!-- 3.x 版本 NameNode Web 端口为 9870,2.x 版本为 50070,写错会打不开管理界面 --> <property> <name>dfs.namenode.http-address</name> <value>localhost:9870</value> </property> </configuration>yarn-site.xml里配置资源管理和调度器:
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> </configuration>第二个参数值得多说一句:伪分布式下MapReduce任务经常因为虚拟内存超限被Kill,直接把检查关掉能省很多调试时间。另外,启动前务必执行hdfs namenode -format格式化,否则NameNode启动失败,这个步骤漏掉的情况在实操中出现率非常高。
3. 用SpringBoot把分析能力包成服务:工程结构、依赖与配置
3.1 SpringBoot在这里扮演的角色不是“大数据处理者”
很多初学者有个误会,以为SpringBoot工程里要写Hadoop的MapReduce任务逻辑。实际上,MapReduce任务是以独立JAR包形式通过hadoop jar命令提交到YARN上运行的,SpringBoot工程只通过HDFS的Java API读取计算结果文件,或者直接查询MySQL中的汇总表。把这两层职责分开,你的项目架构才是对的。
SpringBoot工程内部再分层:Controller负责向外暴露REST接口,Service层做业务组装与指标计算,Mapper层访问MySQL中的结果表,另外单独建一个HdfsClient组件负责从HDFS读取数据文件。这样当Hadoop侧计算逻辑调整时,SpringBoot不需要重新打包。
工程建议用Maven管理,JDK用1.8,SpringBoot版本用2.3.x或2.5.x,这两个版本对Hadoop客户端的兼容性最稳。Hadoop 3.x的客户端依赖与SpringBoot 2.7以下版本集成时会有一些类冲突,最常见的是javax.servlet包名冲突,后续避坑章节会详细说明。
3.2 SpringBoot工程的核心依赖配置
在pom.xml中引入Hadoop客户端依赖和MySQL驱动:
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>3.3.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency>Hadoop客户端依赖会传递引入大量包,跟SpringBoot自带的spring-boot-starter-web存在冲突风险。常见解法是排除掉Hadoop客户端里的javax.servlet相关依赖,或者统一用provided作用域。我一般会这样处理:
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>3.3.4</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency>在application.yml中配置HDFS地址和MySQL连接参数:
hadoop: fs: defaultFS: hdfs://localhost:9000 spring: datasource: url: jdbc:mysql://localhost:3306/power_analysis?useUnicode=true&characterEncoding=utf8 username: root password: root配置写成自定义前缀hadoop.fs而不是spring.hadoop,原因是SpringBoot官方并没有提供Hadoop的自动配置器,自定义前缀更清晰,读取时也方便。
3.3 编写一个读取HDFS文件的最小Service
读取HDFS上的统计分析结果文件,通过FileSystemAPI即可完成,不需要任何MapReduce代码:
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; @Service public class HdfsReadService { @Value("${hadoop.fs.defaultFS}") private String defaultFS; public List<String> readFile(String filePath) throws Exception { Configuration conf = new Configuration(); conf.set("fs.defaultFS", defaultFS); FileSystem fs = FileSystem.get(conf); Path path = new Path(filePath); if (!fs.exists(path)) { throw new RuntimeException("HDFS 文件不存在: " + filePath); } BufferedReader reader = new BufferedReader(new InputStreamReader(fs.open(path))); List<String> lines = new ArrayList<>(); String line; while ((line = reader.readLine()) != null) { lines.add(line); } reader.close(); fs.close(); return lines; } }这里的核心点是FileSystem.get(conf)默认会读取当前Classpath下的core-site.xml,如果你在SpringBoot的resources目录里放了Hadoop配置文件,它会自动加载,无需手工指定。如果你的工程里没有放这些配置,就必须在代码里逐个conf.set设置,否则会连接file:///本地文件系统,报FileNotFoundException。代码里对文件是否存在做了判断,这是为了运行时暴露问题而不是等到解析空列表时才报错。
Service写好后,Controller层直接返回结果字符串列表即可:
@RestController @RequestMapping("/api/power") public class PowerDataController { @Autowired private HdfsReadService hdfsReadService; @GetMapping("/daily/{date}") public Result<List<String>> dailySummary(@PathVariable String date) throws Exception { List<String> lines = hdfsReadService.readFile("/output/daily/" + date + "/part-r-00000"); return Result.success(lines); } }Controller保持轻薄,所有HDFS操作和业务组装下沉到Service层。返回结果里包装一个统一的Result对象,方便前端接收。
4. 数据导入与分析任务:用MapReduce算出电力指标
4.1 从原始CSV到HDFS:导入命令与目录规范
电力采集数据一般是CSV格式,包含字段:采集时间、电厂编号、机组编号、有功功率、无功功率、发电量、电压、电流、温度。开发阶段,你可以用脚本生成模拟数据,也可以用GitHub上开源的公开电力数据集,但需要注意字段对齐。我通常用Python脚本生成测试数据,数据量为几十万条,完全够演示。
将本地CSV上传到HDFS的命令:
# 创建按日期分区的目录 hdfs dfs -mkdir -p /power_data/origin/2025/06/01 # 上传本地数据文件到HDFS hdfs dfs -put /opt/data/station_20250601.csv /power_data/origin/2025/06/01/ # 验证文件块大小与副本状态 hdfs dfs -ls /power_data/origin/2025/06/01/这里有个约定俗成的规矩:原始数据目录用origin标记,分析结果目录统一放在/output下,两者隔离。如果上传文件很大,可以通过-D dfs.block.size=134217728指定块大小,但开发环境默认128MB即可,不用调。
4.2 编写电厂日发电量统计的MapReduce作业
以“统计每个电厂每天的发电量、平均功率、最大功率”为例,写一个可运行的MapReduce作业。Mapper负责解析CSV并输出以电厂编号为Key、发电量指标为Value:
import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class StationDailyMapper extends Mapper<LongWritable, Text, Text, Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); // 跳过CSV表头 if (line.startsWith("collect_time")) { return; } String[] fields = line.split(","); if (fields.length < 6) { return; } String stationId = fields[1]; String date = fields[0].substring(0, 10); String activePower = fields[3]; String generation = fields[5]; // 中间用制表符分隔,Reducer里更方便拆分 context.write(new Text(stationId + "_" + date), new Text(activePower + "\t" + generation)); } }这里把stationId和date拼成组合Key,目的是让同一个电厂同一天的数据进入同一个Reducer。fields[0].substring(0, 10)处理的是yyyy-MM-dd HH:mm:ss格式的采集时间,如果你的数据格式不同,需要调整下标或引入SimpleDateFormat解析。字段下标顺序必须与CSV表头严格对应,这是MapReduce作业最常见的出错原因。
Reducer负责汇总:
import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; public class StationDailyReducer extends Reducer<Text, Text, Text, NullWritable> { @Override protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { double totalGeneration = 0.0; double totalPower = 0.0; double maxPower = 0.0; int count = 0; for (Text value : values) { String[] parts = value.toString().split("\t"); double activePower = Double.parseDouble(parts[0]); double generation = Double.parseDouble(parts[1]); totalGeneration += generation; totalPower += activePower; maxPower = Math.max(maxPower, activePower); count++; } double avgPower = totalPower / count; String[] keyParts = key.toString().split("_"); // 输出格式:日期,电厂编号,总发电量,平均功率,最大功率 String output = keyParts[1] + "," + keyParts[0] + "," + totalGeneration + "," + avgPower + "," + maxPower; context.write(new Text(output), NullWritable.get()); } }Reducer里的totalGeneration用的是double累加,如果数据量到亿级,double精度会丢。更严谨的做法是用BigDecimal,但MapReduce里每一条记录都new BigDecimal会拖慢速度,实际项目里通常用double接受微小误差,如果你需要精确到小数点后两位再在SpringBoot层做处理。
最后写一个Runner类提交作业:
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class StationDailyJob { public static void main(String[] args) throws Exception { if (args.length != 2) { System.err.println("Usage: StationDailyJob <inputPath> <outputPath>"); System.exit(1); } Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "station-daily-summary"); job.setJarByClass(StationDailyJob.class); job.setMapperClass(StationDailyMapper.class); job.setReducerClass(StationDailyReducer.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(NullWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }Runner类里job.setJarByClass这行是必须的,它决定了作业在YARN上运行时如何定位JAR包。直接点击IDE运行不会自动打包,要么用mvn package生成JAR后通过hadoop jar提交,要么在IDE中把mapreduce.job.jar配置指向target目录下的JAR文件。
4.3 提交作业与结果回读MySQL
使用Maven打成JAR包并提交到YARN:
mvn clean package -DskipTests hadoop jar target/power-analysis-1.0-SNAPSHOT.jar \ com.demo.power.job.StationDailyJob \ /power_data/origin/2025/06/01 \ /output/daily/20250601任务跑完之后,结果文件在/output/daily/20250601/part-r-00000。注意同一个输出目录第二次运行时一定会报错,因为MapReduce要求输出目录预先不存在。后面会有专门的方案解决重复运行问题。读取结果并写入MySQL,我一般用一个独立的DataSyncService:
// 伪代码示意,完整代码按你的工程结构组织 public void syncDailySummary(String date) throws Exception { List<String> lines = hdfsReadService.readFile("/output/daily/" + date + "/part-r-00000"); for (String line : lines) { String[] parts = line.split(","); // parts[0] 日期, parts[1] 电厂编号, parts[2] 发电量, parts[3] 平均功率, parts[4] 最大功率 stationDailyMapper.insertOrUpdate(parts[0], parts[1], Double.parseDouble(parts[2]), Double.parseDouble(parts[3]), Double.parseDouble(parts[4])); } }数据同步策略上,如果目标是MySQL里已经存在的记录就做更新,不存在才插入。实际做法很简单:先按日期和电厂编号查询,有则update,无则insert,不必做复杂的UPSERT语法兼容。
5. Hadoop与SpringBoot整合排查:五个典型翻车现场
5.1 伪分布式重启后,HDFS数据全部“消失”
现象:昨天还能正常访问的HDFS文件,今天启动后全部不存在,NameNode报FileNotFound。
原因:hadoop.tmp.dir没有配置,Hadoop默认使用/tmp/hadoop-${user}目录,系统重启或执行清理命令时删掉了临时目录里的NameNode元数据,导致整个文件系统“恢复出厂设置”。
解决:在core-site.xml里把hadoop.tmp.dir指向持久化目录,比如/opt/hadoop/data/tmp,并保证该目录在重启后依然存在。如果你已经中招且没有重要数据,直接重新hdfs namenode -format再启动即可。血泪教训:开发阶段可以随意格式化,但如果集群上已经积累了真实数据,格式化会把元数据全部清空,等于自杀。
5.2 SpringBoot启动报错:No FileSystem for scheme hdfs
现象:SpringBoot工程启动后,调用HDFS读取接口时报java.io.IOException: No FileSystem for scheme: hdfs。
原因:Hadoop客户端依赖没有把hdfs协议对应的FileSystem实现类加载进来,或者自定义配置未生效。
解决:检查pom.xml中hadoop-client依赖是否完整,然后确认代码里conf.set("fs.defaultFS", "hdfs://localhost:9000")有没有被执行。一个常见误用是直接new Configuration()而不设置任何属性,这样拿到的是默认配置,默认访问的是本地文件系统。另外,如果你的resources目录下有core-site.xml,注意它的fs.defaultFS属性是否与SpringBoot配置冲突,Classpath下的配置文件优先级高于代码里的set操作。
5.3 MapReduce任务子进程反复被Kill或报内存不足
现象:作业提交后,Map阶段任务一个接一个失败,日志显示Container killed by the Application Master或Java heap space。
原因:伪分布式环境下,YARN为每个容器分配默认内存,而本机可用内存有限,多个容器同时启动时内存不够用,任务被强制终止。
解决:在yarn-site.xml中调低容器内存配置:
<property> <name>yarn.nodemanager.resource.memory-mb</name> <value>4096</value> </property> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>256</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>2048</value> </property>另外还有一类玄学问题:有时候任务被Kill是因为虚拟内存检查误判,yarn.nodemanager.vmem-check-enabled设为false就能绕过,前面2.3节已经提到过。如果调完内存后仍然频繁失败,可以在JVM参数里限制Map任务的内存:mapreduce.map.java.opts=-Xmx512m,把堆内存压小,给系统留余地。
5.4 重复运行同一个作业输出目录冲突
现象:修改了代码或者想重新计算某一天的数据,第二次提交时直接报Output directory hdfs://localhost:9000/output/daily/20250601 already exists。
原因:MapReduce的FileOutputFormat默认不允许输出目录已存在,这是为了防止用户误覆盖历史结果而设计的机制。
解决:第一个办法,把输出目录带上时间戳,比如/output/daily/20250601_v2,简单但会产生垃圾目录;第二个办法,提交前删除历史路径:
hdfs dfs -rm -r /output/daily/20250601如果希望自动化,在Runner类的main方法里先检查并删除旧目录,再从SpringBoot发起作业提交。我自己更常采用的办法是输出目录固定,但在任务启动前显式清理,这样MySQL增量同步逻辑不会因为目录名变化而需要改参数。
5.5 NameNode启动成功但Web界面打不开
现象:jps能看到NameNode进程,hdfs dfs -ls /也能正常执行,但浏览器访问http://localhost:9870就是打不开。
原因:Hadoop 3.x的NameNode HTTP默认端口是9870,2.x是50070,端口配置错误或者防火墙未放行都会导致访问失败。
解决:先用ss -tlnp | grep java查看NameNode实际监听的端口,再对比hdfs-site.xml里的dfs.namenode.http-address配置。如果你在云服务器上运行,还要检查安全组是否放行了对应端口。另外有一种很少见但确实存在的情况:NameNode绑定了内网IP而非0.0.0.0,外部怎么都访问不了,这时把dfs.namenode.http-bind-host设为0.0.0.0即可,不过伪分布式本机访问不需要改这个。
6. 让系统从演示走向可用:幂等任务与自动化调度
到了这个阶段,大部分人的系统能跑通一次完整链路:原始数据入库分析、结果写入MySQL、接口输出展示。但演示时最尴尬的场景是在评审老师面前重跑一遍批处理,结果因为输出目录冲突或者数据半同步问题而翻车。解决这个问题,需要引入两个工程化习惯:幂等任务设计和自动化调度。
幂等设计的核心是让同一个任务无论执行多少次,结果都保持一致。MapReduce作业在提交前自动清理输出目录,代码里加一段逻辑:
# 封装一个执行分析的脚本,避免手工操作 #!/bin/bash DATE=$1 hdfs dfs -rm -r /output/daily/$DATE 2>/dev/null hadoop jar target/power-analysis-1.0-SNAPSHOT.jar \ com.demo.power.job.StationDailyJob \ /power_data/origin/$DATE \ /output/daily/$DATE这样每次执行都是全量重新计算,不会出现旧结果残留。代价是计算资源浪费,但针对演示项目来说稳定性远胜效率。
MySQL侧的同步同样要幂等:用日期和电厂编号作为唯一键,先查后改。我在station_daily_summary表上建了一个联合唯一索引(stat_date, station_id),插入时走ON DUPLICATE KEY UPDATE,而不是先查一遍再更新,效率高一些也少一次网络往返。SQL里这样写:
INSERT INTO station_daily_summary (stat_date, station_id, total_generation, avg_power, max_power) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE total_generation = VALUES(total_generation), avg_power = VALUES(avg_power), max_power = VALUES(max_power);自动化调度方面,如果你是伪分布式环境,Linux Crontab是成本最低的方案。我的习惯是每天凌晨1点执行分析脚本,凌晨2点执行数据同步到MySQL,白天的SpringBoot接口永远只读昨天的汇总结果。Crontab配置示例:
# 每天凌晨1点重算前一天的电力数据 0 1 * * * /opt/scripts/run_power_analysis.sh $(date -d "yesterday" +%Y%m%d) >> /opt/logs/power_job.log 2>&1上线前务必手动跑一次确认脚本路径和日志权限,否则第二天打开日志发现只有一行Permission denied,那种感觉比项目答辩被问住还难受。真正完整的项目里还会加一个定时任务框架预留:在SpringBoot里配置@Scheduled的肉眼看是空缺的,这是因为调度实际上应该由Hadoop侧来触发,SpringBoot只负责展示和同步结果。这样设计的好处是职责单一,Hadoop计算层、SpringBoot应用层、MySQL存储层互不干扰,每一块都能独立替换。
这个方案走到最后,你手里的成果不是一个CRUD管理系统,而是一条从数据接入、分布式计算到接口服务的完整链路。把这张链路讲清楚,比堆砌任何花哨功能都有说服力。希望帮到你。
本文还有配套的精品资源,点击获取