资讯动态

Hadoop MapReduce伪分布式环境搭建与WordCount实战指南

发布时间:2026/10/9 3:34:53 来源:尧图企业网站定制
简介面向云计算与Hadoop初学者的完整实验记录再现了在Linux环境下从零完成MapReduce程序开发的全过程。实验以triangle.java为样例覆盖vim编辑、CLASSPATH设置、javac编译、jar打包以及通过hadoop jar提交作业等关键环节同时演示了Hadoop自带wordcount程序在HDFS上的运行与结果读取适合正在学习分布式计算或需要对照实验步骤进行复现的学生参考。资源为单个PDF文件约603KB内容紧凑包含实验目的、算法分析、详细操作步骤、运行截图与结果输出以及作者对Hadoop和MapReduce工作机制的总结思考可直接作为实验报告模板或复习资料使用。已有382人学习下载从编辑Java文件到打包执行再到wordcount统计词频这份PDF可以帮助读者快速建立MapReduce编程模型的操作认知规避环境配置与打包环节的常见错误。1. 一次“实验报告三”式的Hadoop MapReduce运行从环境到作业的完整链条做云计算技术实验报告三运行Hadoop MapReduce程序这个实验最大的坎往往不是Map和Reduce函数怎么写而是环境起不来、路径对不上、作业卡在ACCEPTED。这个实验真正考的不是代码而是你有没有把HDFS、YARN和分布式文件路径这一整条链路串起来。这篇文章按一条可复现的路径来拆伪分布式环境怎么搭、输入数据怎么进HDFS、InputSplit怎么决定Map数、WordCount怎么提交运行以及五个反复出现的坑。适合正在交实验报告、准备hadoop课程设计或者被InputSplit这类hadoop面试题问住的读者。先说明一点伪分布式不是缩水版集群而是把集群角色拆成独立进程在一台机器上实打实跑一遍分布式调度链路。把这套流程跑通你看集群日志和分析问题的思路就通了。2. 伪分布式Hadoop实验环境为什么选它以及虚拟机上怎么搭2.1 伪分布式与集群怎么选实验环境的三条底线有读者上来就问实验报告里写“运行Hadoop MapReduce程序”我能不能用单机模式单机模式Local模式里Map和Reduce在同一个JVM里跑完不需要HDFS也不需要YARN它只能验证业务逻辑不能演示任何分布式行为。做这个实验的核心是观察作业如何被提交、分片、调度和执行单机模式给不了这些证据。真正集群又太沉。三台以上虚拟机做hadoop集群搭建时间大多花在SSH免密、节点同步和NTP对齐上真正留给MapReduce的窗口很少。伪分布式是“hadoop伪分布式搭建”最常见的落点一台机器上同时跑NameNode、DataNode、ResourceManager、NodeManager进程行为和集群一致成本却低一个数量级。如果你准备的是hadoop课程设计并且有加分项再考虑扩展到多节点和HA高可用但实验主体用伪分布式完全够。选型时我一般守住三条底线。第一伪分布式必须把各个守护进程分开启动jps一查少一个进程就算没搭好不能假装成功。第二内存规划比配置本身更重要虚拟机总共两个G还硬塞四个守护进程最后一定有一个进程被OOM干掉作业开始前先看free -g。第三所有路径固定下来就别反复改hadoop.tmp.dir、HDFS目录、输出目录这些变量一旦漂移格式化集群和节点数据对不上会让你白白浪费一个晚上。这三点在后面几章都会反复提到。2.2 在Linux虚拟机上搭伪分布式关键配置与启动验证环境准备这一步比较机械但我还是建议别跳。用虚拟机装一个Linux发行版装好JDK并配置JAVA_HOME下载实验指导书指定的Hadoop稳定版本并解压到固定目录。配置HADOOP_HOME和PATH这一步网上说法很多我直接按最简方式写进用户环境变量。如果你的环境用的是hadoop的docker镜像注意容器里没有systemd需要把端口映射出来、数据目录挂载到宿主机下面这些配置文件的内容依然适用。先编辑core-site.xml。这个文件决定性的是“默认文件系统”和“临时数据根目录”configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/home/hadoop/hdata/value /property /configurationfs.defaultFS告诉客户端NameNode在哪儿9000是HDFS RPC通信的默认端口。hadoop.tmp.dir是NameNode元数据和DataNode块数据的根目录必须改成你自己的路径否则默认落在系统的/tmp目录重启一次虚拟机数据就被清空这是后面格式化玄学的主要源头。这个目录最好提前用mkdir -p创建好并确认归属当前启动用户。接着是hdfs-site.xml核心是副本数和角色数据目录configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name valuefile:///home/hadoop/hdata/name/value /property property namedfs.datanode.data.dir/name valuefile:///home/hadoop/hdata/data/value /property /configuration伪分布式只有一台DataNode副本数必须设为1否则HDFS会一直尝试把副本复制到第二台节点然后不停报块不足。name.dir和data.dir把hadoop.tmp.dir细化到两个角色我习惯把它们显式写出来避免Hadoop默认目录和手工操作不一致。这里注意file://前缀不能丢它表示这是本地磁盘路径。mapred-site.xml决定MapReduce跑在哪个框架上configuration property namemapreduce.framework.name/name valueyarn/value /property property namemapreduce.jobhistory.address/name valuelocalhost:10020/value /property /configurationmapreduce.framework.name必须设为yarn作业才会提交给YARN调度否则仍然退回local模式你会在日志里看到“Running job in local mode”但进程表现完全不像分布式。jobhistory是历史服务器记录跑过的作业详情实验报告里要截图时很有用Hadoop 3默认端口是10020启动命令是mapred --daemon start historyserver。最后是yarn-site.xml这里的内存参数是我踩坑最多的位置configuration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.nodemanager.resource.memory-mb/name value2048/value /property property nameyarn.scheduler.maximum-allocation-mb/name value2048/value /property /configurationaux-services必须叫mapreduce_shuffle这是Map和Reduce中间数据交换的通道拼错一个字母作业永远卡在Map 100%但Reduce起不来。内存参数按虚拟机实际大小调整总共4G内存就分2G给YARN总共2G就分1G要留内存给操作系统、HDFS守护进程和你的JVM。调大了任务一直ACCEPTED调小了容器刚启动就被杀这两个方向我都试过。配置完成后按顺序启动export HADOOP_HOME/opt/hadoop export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin # 首次启动前格式化NameNode只做一次 hdfs namenode -format # 启动HDFS与YARN start-dfs.sh start-yarn.sh # 查看进程是否齐全 jps格式化命令只允许在首次启动前执行一次。重复格式化会让NameNode的clusterID与DataNode记录不一致DataNode起不来这是入坑第一课。jps输出里应该能看到NameNode、DataNode、ResourceManager、NodeManager这四个常驻进程有SecondaryNameNode也正常。看到进程后浏览器打开http://localhost:9870HDFS页面和http://localhost:8088YARN任务页面能打开页面才算环境真正就绪。如果页面打不开先回头查进程而不是去改防火墙。提示把HADOOP_HOME写进/etc/profile或者~/.bashrc避免每次开新终端都要export一遍。实验过程中新开窗口连不上hdfs命令八成是这一步漏了。3. 把数据喂给MapReduceHDFS上传与InputSplit的取舍3.1 先建立实验目录HDFS的mkdir/put/ls操作MapReduce读的是HDFS而不是本地磁盘。很多新手把输入文件放在Linux本地就提交作业日志里直接报“Input path does not exist”然后开始怀疑代码有bug实际上代码根本没问题是数据没进分布式存储。做这种hdfs和mapreduce综合实训类的作业第一步永远是“把文件交给HDFS”。一句话说清HDFS路径和本地路径的区别hdfs dfs命令操作的是分布式文件系统路径以hdfs://开头或者直接用/开头Linux命令操作的是本地文件系统路径以/home或/usr这类开头。同一个斜杠在不同命令里指向完全不同的空间。先做最小集操作# 在本地准备一个文本文件 words.txt随便写几行英文单词 # 创建HDFS输入目录父目录不存在会自动创建 hdfs dfs -mkdir -p /exp/input # 上传本地文件到HDFS的/exp/input目录 hdfs dfs -put ./words.txt /exp/input/ # 确认上传成功 hdfs dfs -ls /exp/input # 从HDFS读回文件内容验证文件没传错 hdfs dfs -cat /exp/input/words.txt | head -10三条命令对应三个常见错误。mkdir少了-p父目录不存在直接报错put的源路径是本地、目标路径是HDFS写反了会把HDFS路径当作本地路径报“No such file or directory”ls后面跟的路径决定你看的是根目录还是用户目录裸写exp/input会被解析成/user/hadoop/exp/input和你上传的根目录/exp/input完全不是一回事。传完文件后我习惯用fsck看一眼块分布hdfs fsck /exp/input/words.txt -files -blocks这条命令会列出文件被切成了多少块、每个块在哪个DataNode上。做实验报告时截图这一张能说明你确实理解了“文件在HDFS上是分块存储的”比贴100行代码管用。块是物理存储概念一个小文件只有一个块这为下一个问题埋下伏笔。3.2 InputSplit是什么分片如何决定Map数如果你准备过hadoop面试题大概率遇到过这道题“在一个运行的Hadoop任务中什么是InputSplit”面试官要的并不仅是一句定义而是你能把分片和Map任务的关系讲清楚。一句话版本InputSplit是MapReduce框架对输入文件的逻辑分片一个分片交给一个Map任务处理分片边界由InputFormat计算得出。默认的TextInputFormat按字节偏移切分每个分片尽量对齐一个HDFS块。假设块大小是128MB一个128MB的文件就是1个分片对应1个Map一个250MB的文件会切成3个分片对应3个Map其中最后一个分片很小但也要占一个Map任务。分片只描述“逻辑范围”不真正复制数据Map任务启动后通过RecordReader按分片范围去对应的DataNode上读块。这就解释了为什么小文件多是MapReduce的敌人。实验里如果往里塞几千个几KB的小文件每个文件至少产生一个分片一个分片至少启动一个Map任务任务启动和容器调度的开销远超处理本身。我让读者做过一个对比练习把words.txt复制成10份再传进HDFS跑同一个WordCount观察YARN页面上Map任务数是10还是1亲眼看到数据后“分片决定Map数”这句话就不再是背概念。小文件问题的常见解法是合并输入job.setInputFormatClass(CombineTextInputFormat.class); CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);这段写在Driver里设置合并分片的最大字节数4MB换算成整数是4194304。小于该值的多个文件会被合并成一个逻辑分片减少Map任务数量。注意它只改变逻辑分片不改变HDFS上的块存储文件物理上还是各自独立的块。4. 提交并运行MapReduce程序从WordCount到hadoop jar的落地命令4.1 一个能交差的MapReduce程序WordCount源码逐段讲WordCount是mapreduce编程实例里的Hello World也是各类头歌实验3 mapreduce基础编程最常见的题目。下面这个版本保留了教材风格重点是让你看清Map、Reduce、Driver三部分的职责。如果用的是实验平台里的在线实训环境代码骨架通常已经给出你补全三处就行但理解逻辑比补代码更重要。import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCount { public static class TokenizerMapper extends MapperObject, Text, Text, IntWritable { private final static IntWritable ONE new IntWritable(1); private Text word new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { StringTokenizer itr new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, ONE); } } } public static class IntSumReducer extends ReducerText, IntWritable, Text, IntWritable { private IntWritable result new IntWritable(); public void reduce(Text key, IterableIntWritable values, Context context) throws IOException, InterruptedException { int sum 0; for (IntWritable val : values) { sum val.get(); } result.set(sum); context.write(key, result); } } public static void main(String[] args) throws Exception { Configuration conf new Configuration(); Job job Job.getInstance(conf, word count); job.setJarByClass(WordCount.class); job.setMapperClass(TokenizerMapper.class); job.setCombinerClass(IntSumReducer.class); job.setReducerClass(IntSumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }逻辑拆开看。Mapper的输入是Object key和Text valuekey是行首字节偏移量value是当前行文本这是TextInputFormat默认喂给Map的数据格式。map方法里用StringTokenizer把一行按空白切单词每切出一个单词就写一次计数1所以“hello”出现3次就会产生3条“hello,1”的中间结果。Reducer收的是同一个单词的value列表在reduce里累加后写出一条“单词,总数”。这里的核心机制是shuffleMap输出的中间键值对经过分区、排序、归并后同一个key的所有value必然进入同一个reduce调用这个机制不需要你手写任何代码。Combiner设成和Reducer同一个类这是WordCount特有的写法。Combiner在Map端先做一次“同类相加”把10条“hello,1”压成一条“hello,10”大幅减少shuffle阶段网络传输的数据量。这种手段在真实作业里是常规优化实验报告里写上这个设置能体现你懂优化而不是只会跑通。4.2 编译打包classpath怎么拼才不翻车Java写完了不能直接hadoop jar要先编译成class再打成jar包。这一步翻车率极高报错集中在“程序包org.apache.hadoop不存在”。原因只有一个javac命令找不到Hadoop依赖jar。Hadoop 3把依赖分散在share/hadoop/common、share/hadoop/common/lib、share/hadoop/mapreduce等目录手工一个个加-classpath一定会漏。我最稳的写法是利用hadoop命令自带的classpath# 编译-d指定class输出目录 javac -classpath $(hadoop classpath) -d ./classes WordCount.java # 进入classes目录把class打包成jar jar -cvf wordcount.jar -C ./classes/ . # 确认jar里的内容 jar -tf wordcount.jar | head$(hadoop classpath)执行后会输出一串完整路径包含Hadoop自身及所有依赖jar这是官方提供的拼路径手段不需要手工维护。jar -cvf的-c表示创建新包-v输出过程-f指定文件名-C先进入classes目录再打包避免把.class文件的父路径也打进jar里。如果你的Hadoop版本不支持hadoop classpath命令退而求其次用通配符拼接javac -classpath $HADOOP_HOME/share/hadoop/common/*:$HADOOP_HOME/share/hadoop/mapreduce/*:$HADOOP_HOME/share/hadoop/common/lib/* -d ./classes WordCount.java注意lib目录必须拼上Hadoop运行时的很多第三方依赖在那里。通配符方案能用但路径一旦写错不会报错只会编译时不认识org.apache.hadoop排查起来更费时间。注意编译之前先确认HADOOP_HOME环境变量已经生效。可以用echo $HADOOP_HOME检查。环境变量没配好hadoop classpath返回空串下面的编译必然失败。这一步在hadoop安装与配置里是常见扣分点。4.3 提交作业hadoop jar与实验报告里的参数说明打包完成输入数据也进了HDFS可以提交作业了。命令格式是hadoop jar jar包 主类名 输入路径 输出路径我显式写出主类名WordCount避免依赖jar包里的Main-Class配置# 提交WordCount输入输出都在HDFS hadoop jar wordcount.jar WordCount /exp/input /exp/output # 运行完成后读回结果 hdfs dfs -cat /exp/output/part-r-00000输出目录/exp/output在运行前必须不存在MapReduce不会覆盖已有目录这是保护机制而不是bug。运行成功的结果里除了part-r-00000还有一个素_SUCCESS空文件用它判断作业是否成功最直接。part-r-00000里的-r表示来自Reducer如果你把Reduce任务数设成0输出文件会叫part-m-00000看到这个命名变化也是实验报告里值得写一句的观察点。运行中想看进度打开http://localhost:8088找到对应的Application ID点进详情能看到Map和Reduce分阶段进度、容器日志入口、计数器列表。作业状态从ACCEPTED到RUNNING再到SUCCEEDED如果长时间停在ACCEPTED问题大概率出在第2章配置的YARN内存参数上。实验报告通常需要过程证据我一般会顺手把作业日志落盘# 通过applicationId拉取完整日志 yarn logs -applicationId application_123456789_0001 /home/hadoop/yarn-app.logapplicationId可以在YARN页面看到也可以用yarn application -list -appStates ALL查询。这份日志里包含了每个任务的容器信息、Map和Reduce的计数器、GC时间等明细截图进报告比单纯贴命令有说服力得多。5. MapReduce实验的5个高频坑从起不来节点到任务直接失败5.1 集群起不来NameNode/DataNode进程消失与hostname玄学第一条坑执行start-dfs.sh时提示找不到主机名或者NameNode进程启动几秒后消失。查logs目录下的hadoop-hadoop-namenode-*.log看到UnknownHostException。原因Linux的hostname没有写进/etc/hosts。Hadoop内部用hostname标识节点NameNode启动时解析自己的主机名失败就直接退出。这是虚拟机装hadoop最常见的问题不是Hadoop配置写错而是操作系统的主机名映射没配好。解决把hostname改成简单单词比如hadoop然后在/etc/hosts里加一行“127.0.0.1 hadoop”重启网络服务或者直接重启虚拟机再重新格式化NameNode启动。这个坑我见过不少人反复重装系统其实一行配置的事。第二条坑DataNode起不来日志里报Incompatible namespaceID。原因NameNode被格式化过多次每次格式化会生成新的clusterID而DataNode的数据目录里还保存着旧clusterID两边无法匹配。这类异常几乎都发生在你执行了第二次hdfs namenode -format之后。解决停掉所有Hadoop进程删除hadoop.tmp.dir下所有数据包括name和data两个子目录然后只格式化一次再启动。格式化是“后悔药”但吃了后悔药必须连DataNode目录一起清掉只清NameNode不清DataNode换了新clusterID的NameNode和旧clusterID的DataNode永远谈不拢。5.2 提交作业就失败路径、权限和YARN的坑第三条坑提交WordCount时报“Input path does not exist: /exp/input”但用hdfs dfs -ls又能看到这个目录。原因HDFS路径的解析规则坑了你。不带前缀的路径会被自动加上/user/当前用户名/比如exp/input实际指向/user/hadoop/exp/input而带一个斜杠的/exp/input才是根目录下的目录。两个路径长得像指向完全不同的位置。还有一种情况是客户端当前用户不是HDFS上的属主权限不足读不到目录但最典型的就是路径前缀问题。解决所有命令和提交参数统一写成绝对路径用/开头并且保持一致。提交前用hdfs dfs -ls /exp/input确认输入存在再hadoop jar提交同样的路径两头一致就不会再出这个错。第四条坑作业一直ACCEPTED不进入RUNNINGYARN页面上NodeManager显示Lost。原因资源不够。yarn.nodemanager.resource.memory-mb设成了4096但虚拟机总共只有2G内存NodeManager向ResourceManager上报的资源永远无法满足容器申请失败作业只能停在ACCEPTED。解决调小yarn-site.xml里的nodemanager.resource.memory-mb和scheduler.maximum-allocation-mb保持一致改成2048或者更低然后重启YARN。这个现象也在hadoop和zookeeper整合实战里出现过多部署一个ZooKeeper进程就再挤掉几百MB内存规划必须通盘考虑。5.3 结果不对或输出为空Output目录与Reducer有效性第五条坑第二次运行同一个作业报“Output directory /exp/output already exists”。原因MapReduce框架拒绝覆盖输出目录这是防止误删历史结果的安全保护。很多作业失败不是代码问题而是忘了清理旧输出。解决运行前清掉旧目录hdfs dfs -rm -r /exp/output或者每次换新的输出目录名。我一般会写一个run.sh在里面先删除输出路径再提交作业开头加一行注释说明这个删除只针对输出目录避免手滑把输入目录也删了。反复调试时这个脚本能省不少事。如果你遇到作业SUCCEEDED但part文件为空这个现象我提一下排查方向先看Web UI里的计数器确认map output records有没有数值。如果Map输出了数据而Reduce输入分组为零问题大概率出在key类型或序列化配置上常见的是setOutputKeyClass写错导致Shuffle阶段key不能正确比较。这时候去查4.1的Driver代码三行而不是去改Map逻辑方向对了问题很快就定位。6. 用实验报告的口径验证结果日志、计数器与一次调优6.1 三处必须截图验证的证据交实验报告三光有part-r-00000的输出结果远远不够你需要的是过程证据。我的习惯是按顺序截三张图第一张是作业运行完毕的终端输出包含作业ID和SUCCEEDED状态第二张是YARN Web UI的Application详情页上面有Map和Reduce的进度条以及完整计时第三张是yarn logs落盘后的关键行。这三张图能证明作业不是伪造的。验证是否把数据跑对靠计数器。下表列出最常用的三个计数器名含义判断标准map input records输入到Map的总行数应与输入文件行数一致map output recordsMap写出的中间键值对数应等于所有单词出现次数reduce output recordsReduce输出的最终条数应等于去重后的单词数在Web UI点击Application后进入Counters页签能看到上述数值。如果map input records等所有计数器全为零先怀疑输入路径如果map有输出但reduce没有再去查shuffle配置。6.2 一次有效的调优自定义Reducer与对比观察跑通默认配置只是及格线。我建议做一次有数据支撑的调优把Reducer数量从默认改成2观察输出和耗时的变化。hadoop jar wordcount.jar WordCount -D mapreduce.job.reduces2 /exp/input /exp/output2加了-D mapreduce.job.reduces2后输出目录里会出现part-r-00000和part-r-00001两个文件。对几百KB的实验文件耗时差异几乎为0但你能看到shuffle分区行为文件命名规则也变了。更值得做的是把输入从TextInputFormat换成CombineTextInputFormat再跑一次观察Map任务数量减少的比例实验报告的“调优手段”一栏就能写实而不是空洞地写“优化了代码”。我第一次做这个实验时只顾着高亮SUCCEEDED截图忘了留计数器页结果被要求返工补材料。从那以后我固定顺序先截Web UI实时进度把yarn日志落盘保存再清理输出目录换下一组参数顺序一次都不能反。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑