简介:这是一套面向高校计算机相关专业学生的Hadoop大数据开发实战项目资料,以数据云盘系统为业务场景,适合用作课程设计、期末大作业或毕业设计参考,也便于新手通过完整项目理解大数据开发流程。压缩包共126个文件,约58.11MB,以32个Java源文件为核心,配合10个JSP页面、19个JavaScript脚本、11个CSS样式及10个XML配置,另有jar依赖、properties配置、字体图标与图片资源,构成一套可部署运行的Web应用工程。项目代码注释较为完整,功能覆盖数据云盘的核心业务,界面美观、操作简单、管理便捷,具有较高的实际应用价值。目前已有1153人学习下载,说明其参考热度较高。下载后按文档说明简单部署即可运行,既能帮助读者快速搭建可演示的系统,也便于对照源码梳理模块划分、理解前后端交互与大数据组件集成思路,为课程答辩或后续二次开发提供扎实基础。
1. 数据云盘项目到底在练什么:从 Hadoop 伪分布式到可演示的网盘后端
很多人第一次看到「Hadoop 大数据开发项目实战数据云盘项目」这个标题,会以为它只是把文件传到 HDFS 上就完事了。真动手做一遍才发现,它其实是一条完整的链路:用户在前端上传文件,后端把文件写进 HDFS,元数据落到 MySQL,再通过 MapReduce 或 Spark 做文件统计、去重、容量分析,最后还要有一套能跑起来的文档说明,让答辩老师或者面试官三分钟看懂你做了什么。这个项目适合两类人:一类是大数据课程设计、毕业设计需要交出一个完整可演示系统的同学;另一类是想从「只会敲 hadoop fs -put」进阶到「能说清 NameNode、DataNode、ResourceManager 怎么协作」的开发者。它不追求高并发生产级,但要求你真正理解 HDFS 的读写路径、YARN 的任务调度,以及一个云盘类应用的最小闭环。下面我按自己带人做这个项目的顺序,把选型、搭建、编码、排错和进阶验证拆开讲。
2. 先定架构再动手:数据云盘项目的最小可行技术栈
2.1 为什么选 Hadoop 伪分布式而不是单机模式
单机模式跑 MapReduce 用的是本地文件系统,看不到 DataNode 的块副本机制,也测不出 YARN 的资源调度。伪分布式虽然只在一台机器上,但 NameNode、DataNode、ResourceManager、NodeManager 都是独立进程,HDFS 的块大小、副本数、RPC 端口全部按真实集群的方式工作。对于数据云盘项目,文件上传后要能通过 Web 界面看到块分布和副本状态,伪分布式是成本最低又能体现「大数据」特征的选择。常见做法是:开发阶段用伪分布式,答辩演示前如果机器够,再扩成三节点完全分布式,代码基本不用改,只改 core-site.xml 里的 fs.defaultFS 指向。
2.2 数据云盘的功能模块拆解
一个能拿得出手的数据云盘,至少包含四个模块。第一是用户模块,注册登录、配额管理,元数据存 MySQL。第二是文件模块,上传、下载、删除、重命名、目录树展示,文件实体存 HDFS。第三是分析模块,用 MapReduce 统计每个用户的文件总大小、文件类型分布、重复文件检测。第四是回收站,删除的文件先移到 HDFS 的 /trash 目录,保留七天再物理删除。这四个模块里,文件模块和分析模块是 Hadoop 真正发挥作用的地方,也是答辩时最容易被追问的点。
2.3 环境版本与依赖对照表
版本不统一是新手翻车最多的地方。Hadoop 2.x 和 3.x 的端口、配置文件、API 都有差异,Java 版本也卡得很死。下面这张表是我在多次搭建后固定下来的组合,能避开大部分兼容性问题。
| 组件 | 推荐版本 | 关键说明 |
|---|---|---|
| JDK | 1.8 | Hadoop 3.x 对 JDK 11 支持不完整,1.8 最稳 |
| Hadoop | 3.2.4 | 伪分布式配置成熟,Web UI 端口 9870 |
| MySQL | 5.7 或 8.0 | 存元数据,注意时区和驱动包版本 |
| Maven | 3.6+ | 管理 Hadoop 客户端依赖 |
| Spring Boot | 2.5.x | 后端框架,和 JDK 1.8 匹配 |
提示:不要用最新版 Hadoop 4.x 做课程项目,生态工具链还没跟上,遇到问题搜到的答案大多是 2.x/3.x 的,排查成本极高。
2.4 从零开始安装 Hadoop 伪分布式的关键命令
安装步骤网上很多,我只列容易出错的几个环节。先确认 ssh 免密登录本机已经配好,否则 start-dfs.sh 会反复提示输入密码。然后解压、配环境变量、改五个配置文件。核心是 core-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml 和 workers。
# 解压并设置环境变量 tar -zxvf hadoop-3.2.4.tar.gz -C /opt/ echo 'export HADOOP_HOME=/opt/hadoop-3.2.4' >> /etc/profile echo 'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> /etc/profile source /etc/profile # 配置 ssh 免密 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys # 格式化 NameNode(只执行一次) hdfs namenode -format # 启动 HDFS 和 YARN start-dfs.sh start-yarn.sh # 验证进程 jps逻辑说明:hdfs namenode -format会生成 fsimage,只在首次搭建时执行,重复执行会导致 clusterID 不一致,DataNode 起不来。jps应该看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager 五个进程。参数说明:core-site.xml 里 fs.defaultFS 设为 hdfs://localhost:9000,hdfs-site.xml 里 dfs.replication 设为 1,因为伪分布式只有一个 DataNode,设成 3 会一直报副本不足。
2.5 数据云盘后端接入 HDFS 的 Java API 写法
Spring Boot 项目里操作 HDFS,核心是拿到 FileSystem 对象。不要每次请求都 new 一个 Configuration,连接开销很大,做成单例 Bean 或者用连接池。下面是一个上传文件的最小示例。
// HdfsService.java @Component public class HdfsService { private FileSystem fileSystem; @PostConstruct public void init() throws IOException { Configuration conf = new Configuration(); // 指向伪分布式 NameNode 地址 conf.set("fs.defaultFS", "hdfs://localhost:9000"); // 避免权限问题,本地测试用 System.setProperty("HADOOP_USER_NAME", "root"); this.fileSystem = FileSystem.get(conf); } public void upload(MultipartFile file, String hdfsPath) throws IOException { // 创建目标路径的父目录 Path path = new Path(hdfsPath); if (!fileSystem.exists(path.getParent())) { fileSystem.mkdirs(path.getParent()); } // 写入 HDFS try (FSDataOutputStream out = fileSystem.create(path); InputStream in = file.getInputStream()) { IOUtils.copyBytes(in, out, 4096, false); } } @PreDestroy public void close() throws IOException { if (fileSystem != null) { fileSystem.close(); } } }逻辑说明:FileSystem.get(conf)会根据 fs.defaultFS 自动选择 HDFS 实现。fileSystem.create(path)返回 FSDataOutputStream,默认覆盖已存在文件,如果要追加用 append 方法。IOUtils.copyBytes的第三个参数是缓冲区大小,4096 是保守值,大文件可以调到 8192 或 16384。参数说明:HADOOP_USER_NAME在 Windows 开发环境下必须设,否则会报 Permission denied;Linux 下如果当前用户就是 Hadoop 启动用户,可以省略。
3. 把文件写进 HDFS 之后:元数据、分块与 MapReduce 统计的落地细节
3.1 HDFS 写文件的真实路径与元数据落库时机
很多同学以为文件上传就是调一次 API 就完事,实际上 HDFS 的写入分两步:客户端先向 NameNode 请求创建文件,NameNode 在内存里记录元数据并返回一个 DataNode 列表;客户端再把数据切成块,逐个写到 DataNode 的管道里。数据云盘项目里,MySQL 存的元数据(文件名、大小、上传时间、HDFS 路径)应该在 HDFS 写入成功之后再落库,否则 HDFS 写失败但数据库有记录,就会出现「文件列表里有但下载 404」的经典 bug。我一般用先写 HDFS 再写 MySQL 的顺序,并在 MySQL 写入失败时补偿删除 HDFS 文件。
3.2 用 MapReduce 统计用户文件容量分布
分析模块是体现「大数据」的关键。一个简单的 MapReduce 任务是统计每个用户目录下的文件总大小。输入是 HDFS 上 /userdata 下的所有文件,输出是用户 ID 和总字节数。下面给出 Mapper 和 Reducer 的核心代码。
// SizeMapper.java public class SizeMapper extends Mapper<LongWritable, Text, Text, LongWritable> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { // 输入格式:每行是 "用户ID,文件路径,文件大小" String[] fields = value.toString().split(","); if (fields.length == 3) { context.write(new Text(fields[0]), new LongWritable(Long.parseLong(fields[2]))); } } } // SizeReducer.java public class SizeReducer extends Reducer<Text, LongWritable, Text, LongWritable> { @Override protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { long total = 0; for (LongWritable val : values) { total += val.get(); } context.write(key, new LongWritable(total)); } }逻辑说明:Mapper 按逗号切分,把用户 ID 作为 key,文件大小作为 value 输出。Shuffle 阶段会把同一用户的数据拉到同一个 Reducer。Reducer 累加后输出总大小。参数说明:如果输入文件很大,可以在 Driver 里设置job.setNumReduceTasks(3)来并行,但伪分布式下建议设为 1,避免资源竞争。输出路径不能已存在,否则 Job 会直接抛 FileAlreadyExistsException。
3.3 重复文件检测的 MapReduce 实现思路
数据云盘里用户可能上传同一份文件多次,检测重复可以按文件内容的 MD5 做 key。Mapper 读文件时计算 MD5,输出 <MD5, 文件路径>。Reducer 里如果 values 数量大于 1,就说明有重复。这个任务比容量统计更耗 IO,因为要读文件内容而不是元数据。常见做法是先用 MySQL 里存的文件大小做粗筛,只对大小相同的文件计算 MD5,能减少大量计算。
3.4 回收站机制的 HDFS 实现
HDFS 本身有 Trash 机制,但默认关闭。在 core-site.xml 里设置fs.trash.interval=10080(单位分钟,七天)和fs.trash.checkpoint.interval=0,然后重启 HDFS。之后用hadoop fs -rm删除的文件会移到 /user/root/.Trash 下,而不是直接删掉。数据云盘项目里,前端点删除时调用的就是带 Trash 的删除 API,用户可以在回收站里恢复。注意:FileSystem.delete(path, false)是直接删,FileSystem.delete(path, true)才是移入回收站,这个布尔参数很容易写反。
4. 避坑与排查:数据云盘项目里最容易翻车的五个地方
4.1 DataNode 启动后立刻消失
现象:jps看到 DataNode 进程一闪而过,或者根本没有。原因:多次执行hdfs namenode -format导致 NameNode 和 DataNode 的 clusterID 不一致,DataNode 拒绝注册。解决:删掉所有节点的 data 目录(dfs.datanode.data.dir 配置的路径)和 NameNode 的 name 目录,重新格式化一次,然后只启动一次。血泪经验是格式化命令一辈子只跑一次,除非你确定要清空集群。
4.2 Web UI 打不开 9870 端口
现象:浏览器访问 http://localhost:9870 超时。原因:Hadoop 3.x 的 NameNode Web UI 端口从 50070 改成了 9870,很多老教程还在写 50070。另外防火墙可能没放行。解决:确认 hdfs-site.xml 里 dfs.namenode.http-address 的值,用netstat -tlnp | grep 9870看端口是否监听。如果是云服务器,安全组也要放行。
4.3 MapReduce 任务卡在 map 0% reduce 0%
现象:Job 提交后一直不动,日志里没有明显报错。原因:YARN 的 NodeManager 没启动,或者 mapred-site.xml 里 mapreduce.framework.name 没设成 yarn。解决:jps确认 NodeManager 在,检查 mapred-site.xml 和 yarn-site.xml 的配置是否配对。另一个常见原因是内存不够,在 yarn-site.xml 里把 yarn.nodemanager.resource.memory-mb 调小到 1024 试试。
4.4 Java API 报 Could not obtain block
现象:下载文件时抛异常,提示某个 block 找不到。原因:文件上传后 DataNode 还没完成块复制,客户端立刻去读,或者 DataNode 挂了导致副本丢失。解决:上传后加一个短暂的等待,或者用fileSystem.exists()确认文件可见后再返回给前端。如果是副本丢失,用hdfs fsck /path -files -blocks查看块状态,必要时从其他副本恢复。
4.5 MySQL 元数据和 HDFS 实际文件不一致
现象:文件列表里显示有某个文件,但下载时报 404。原因:HDFS 写入成功但 MySQL 插入失败,或者反过来。解决:在 Service 层用事务包住 MySQL 操作,HDFS 操作放在事务提交前,如果 MySQL 回滚就补偿删除 HDFS 文件。更稳妥的做法是加一个定时对账任务,每天扫描 HDFS 和 MySQL,把不一致的记录清理掉。
5. 让项目从「能跑」到「能讲」:文档说明与答辩演示的进阶技巧
文档说明不是把代码注释复制一遍,而是要讲清楚三件事:系统架构图、核心流程时序、关键配置参数。我一般会在文档里放一张手绘的架构图,标出浏览器、Spring Boot、MySQL、HDFS、YARN 五个框,箭头标清数据流向。然后挑「文件上传」和「容量统计」两个流程,用文字加序号写清每一步调用了什么、参数是什么、失败怎么处理。答辩时老师最爱问的是「你的项目和直接存本地磁盘有什么区别」,标准答案是:HDFS 有副本机制、能横向扩展、支持 MapReduce 并行分析,本地磁盘做不到。
演示环节有一个技巧:提前准备好一个 100MB 左右的测试文件,现场上传,然后立刻在 Web UI 上看块分布,再跑一次容量统计的 MapReduce,让老师看到 Job 从提交到完成的全过程。这比只展示一个静态页面有说服力得多。另外,把hdfs fsck /userdata -files -blocks -locations的输出截图放进文档,能证明你确实理解块和副本的概念。
验证项目是否真正跑通,我习惯用三个检查点。第一,上传一个文件后,用hadoop fs -ls /userdata/用户名能看到文件,且副本数为 1。第二,删除文件后,在 /user/root/.Trash 下能找到,七天后自动清理。第三,MapReduce 统计结果和 MySQL 里累加的文件大小一致。这三个点过了,项目基本就没有暗病。
最后一个习惯:每次改完配置文件,先stop-dfs.sh && stop-yarn.sh,再重新启动,不要热改配置。Hadoop 对配置的加载是启动时一次性的,热改不生效还容易让人怀疑人生。希望帮到你。
本文还有配套的精品资源,点击获取