【集群模式】第一个MapReduce程序——WordCount

【集群模式】第一个MapReduce程序------WordCount_在3节点完全分布式Hadoop集群上运行MapReduce任务


前言

上一篇文章我们完成了Hadoop完全分布式集群的搭建,成功启动了HDFS和YARN。本文将在这个3节点集群上,运行我们的第一个MapReduce程序------WordCount 。本文采用集群模式,直接在Linux集群上通过命令行提交作业,让任务真正分布式运行在YARN上。


一、MapReduce核心原理回顾

一个MapReduce程序主要包括三部分:Mapper类Reducer类执行类(Driver)

1.1 Map阶段

将输入文件的每一行按空格切分,提取单词,转为 <key, 1> 的形式:

  • KEYIN:每行的偏移量(LongWritable)
  • VALUEIN:当前行的文本内容(Text)
  • KEYOUT:切分后的单词(Text)
  • VALUEOUT:固定值 1(LongWritable)

1.2 Reduce阶段

将Map阶段输出的相同key(单词)对应的value(1)进行累加,得到单词出现总次数:

  • KEYIN:单词(Text)
  • VALUEIN:该单词出现次数的集合(Iterable)
  • KEYOUT:单词(Text)
  • VALUEOUT:总次数(LongWritable)

二、集群环境确认

在运行程序前,先确认集群状态正常:

2.1 检查HDFS和YARN是否启动

bash 复制代码
[atguigu@hadoop102 ~]$ jpsall
=============== hadoop102 ===============
1234 NameNode
1567 DataNode
1890 NodeManager
2345 JobHistoryServer
=============== hadoop103 ===============
2341 DataNode
2678 ResourceManager
2890 NodeManager
=============== hadoop104 ===============
3456 DataNode
3789 SecondaryNameNode
3901 NodeManager

2.2 Web页面确认

组件 访问地址 状态
HDFS NameNode http://hadoop102:9870 Live Nodes应为3
YARN ResourceManager http://hadoop103:8088 Active Nodes应为3
JobHistory http://hadoop102:19888 正常访问

三、准备输入数据

3.1 创建HDFS输入目录

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -mkdir -p /wordcount/input

3.2 创建测试文件并上传

在本地创建测试文件:

bash 复制代码
[atguigu@hadoop102 ~]$ vim word.txt

输入内容(模拟大数据技术栈关键词):

复制代码
hadoop hdfs yarn mapreduce
hadoop spark flink kafka
hive hbase sqoop flume
hadoop spark hive pig
java python scala sql
hadoop hdfs hive spark

上传到HDFS:

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -put word.txt /wordcount/input/

3.3 验证上传成功

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -ls /wordcount/input/
Found 1 items
-rw-r--r--   3 atguigu supergroup        120 2024-04-25 09:00 /wordcount/input/word.txt

[atguigu@hadoop102 ~]$ hadoop fs -cat /wordcount/input/word.txt
hadoop hdfs yarn mapreduce
hadoop spark flink kafka
hive hbase sqoop flume
hadoop spark hive pig
java python scala sql
hadoop hdfs hive spark

四、运行官方WordCount示例

Hadoop安装包中自带了WordCount示例jar包,我们先运行官方示例,验证集群是否正常。

4.1 执行命令

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /wordcount/input /wordcount/output

4.2 命令解析

部分 说明
hadoop jar 提交jar包到Hadoop集群运行
/opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar 官方示例jar包路径
wordcount 指定运行WordCount程序
/wordcount/input HDFS上的输入路径
/wordcount/output HDFS上的输出路径(必须不存在,否则报错

4.3 查看执行日志

执行过程中会输出Map和Reduce的进度:

复制代码
2024-04-25 09:05:01 INFO mapreduce.Job: Running job: job_1714012800000_0001
2024-04-25 09:05:10 INFO mapreduce.Job: map 0% reduce 0%
2024-04-25 09:05:18 INFO mapreduce.Job: map 100% reduce 0%
2024-04-25 09:05:25 INFO mapreduce.Job: map 100% reduce 100%
2024-04-25 09:05:26 INFO mapreduce.Job: Job job_1714012800000_0001 completed successfully

4.4 查看输出结果

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -ls /wordcount/output/
Found 2 items
-rw-r--r--   3 atguigu supergroup          0 2024-04-25 09:05 /wordcount/output/_SUCCESS
-rw-r--r--   3 atguigu supergroup        102 2024-04-25 09:05 /wordcount/output/part-r-00000

查看统计结果:

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -cat /wordcount/output/part-r-00000
flink	1
flume	1
hadoop	4
hbase	1
hdfs	2
hive	3
java	1
kafka	1
mapreduce	1
pig	1
python	1
scala	1
spark	3
sqoop	1
sql	1
yarn	1

4.5 输出文件说明

文件名 说明
_SUCCESS 任务执行成功的标志文件,内容为空
part-r-00000 Reduce任务的输出结果文件,r表示Reduce输出,00000表示第0个Reduce任务

如果有多个Reduce任务,会生成 part-r-00000part-r-00001 等多个文件。


五、自定义WordCount程序(Java开发)

官方示例只能按空格切分,实际业务中可能需要自定义分隔符、过滤停用词等。下面我们编写自定义的WordCount程序。

5.1 开发环境准备

在hadoop102上安装Maven(或直接使用IDEA开发后上传jar包):

bash 复制代码
[atguigu@hadoop102 ~]$ sudo yum install -y maven

5.2 创建Maven项目

bash 复制代码
[atguigu@hadoop102 ~]$ mkdir -p ~/projects/wordcount
[atguigu@hadoop102 ~]$ cd ~/projects/wordcount

创建 pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu</groupId>
    <artifactId>wordcount</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <hadoop.version>3.1.3</hadoop.version>
    </properties>

    <dependencies>
        <!-- Hadoop客户端依赖 -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- 日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 打包插件:将依赖一起打包 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

5.3 Hadoop序列化类型说明

Hadoop类型 Java对应类型 说明
Text String 可变长度文本
LongWritable long 长整型
IntWritable int 整型
FloatWritable float 浮点型
DoubleWritable double 双精度浮点型
BooleanWritable boolean 布尔型
NullWritable null 空值
ArrayWritable 数组 数组类型
MapWritable Map Map类型

5.4 编写Mapper类

创建目录结构:

bash 复制代码
[atguigu@hadoop102 wordcount]$ mkdir -p src/main/java/com/atguigu/mapreduce

创建 WordCountMapper.java

java 复制代码
package com.atguigu.mapreduce;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.util.StringUtils;

import java.io.IOException;

/**
 * WordCount Mapper类
 * 
 * 输入:  KEYIN=LongWritable(行偏移量) VALUEIN=Text(一行文本)
 * 输出:  KEYOUT=Text(单词)         VALUEOUT=LongWritable(计数1)
 */
public class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
    
    // 复用对象,减少GC压力
    private Text outKey = new Text();
    private final static LongWritable outValue = new LongWritable(1);

    @Override
    protected void map(LongWritable key, Text value, Context context) 
            throws IOException, InterruptedException {
        
        // 获取一行数据
        String line = value.toString();
        
        // 使用Hadoop的StringUtils.split按空格切分(性能优于Java原生split)
        // 支持多个空格、制表符等空白字符
        String[] words = StringUtils.split(line, ' ');
        
        // 遍历单词,输出 <单词, 1>
        for (String word : words) {
            // 过滤空字符串
            if (word != null && !word.trim().isEmpty()) {
                outKey.set(word.trim().toLowerCase()); // 转为小写,统一统计
                context.write(outKey, outValue);
            }
        }
    }
}

5.5 编写Reducer类

创建 WordCountReducer.java

java 复制代码
package com.atguigu.mapreduce;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * WordCount Reducer类
 * 
 * 输入:  KEYIN=Text(单词)              VALUEIN=Iterable<LongWritable>(计数集合)
 * 输出:  KEYOUT=Text(单词)             VALUEOUT=LongWritable(总次数)
 */
public class WordCountReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
    
    private LongWritable outValue = new LongWritable();

    @Override
    protected void reduce(Text key, Iterable<LongWritable> values, Context context) 
            throws IOException, InterruptedException {
        
        // 累加单词出现次数
        long sum = 0;
        for (LongWritable value : values) {
            sum += value.get();
        }
        
        outValue.set(sum);
        context.write(key, outValue);
    }
}

5.6 编写Driver执行类

创建 WordCountDriver.java

java 复制代码
package com.atguigu.mapreduce;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
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;

import java.io.IOException;

/**
 * WordCount Driver类
 * 配置并提交MapReduce作业
 */
public class WordCountDriver {

    public static void main(String[] args) throws IOException, 
            ClassNotFoundException, InterruptedException {
        
        // 参数校验:需要输入路径和输出路径
        if (args.length != 2) {
            System.err.println("Usage: WordCountDriver <input path> <output path>");
            System.exit(-1);
        }

        // 1. 获取配置和Job对象
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Custom WordCount");
        
        // 2. 设置Jar包路径(集群运行必须)
        job.setJarByClass(WordCountDriver.class);
        
        // 3. 关联Mapper和Reducer
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);
        
        // 4. 设置Map输出KV类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);
        
        // 5. 设置最终输出KV类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);
        
        // 6. 设置输入输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        
        // 7. 提交作业并等待完成
        boolean result = job.waitForCompletion(true);
        
        // 8. 根据结果退出
        System.exit(result ? 0 : 1);
    }
}

5.7 项目完整目录结构

复制代码
wordcount/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── atguigu/
                    └── mapreduce/
                        ├── WordCountMapper.java
                        ├── WordCountReducer.java
                        └── WordCountDriver.java

5.8 编译打包

bash 复制代码
[atguigu@hadoop102 wordcount]$ mvn clean package

打包成功后,会在 target/ 目录生成 wordcount-1.0-SNAPSHOT.jar


六、提交自定义程序到集群运行

6.1 准备新的输入数据

bash 复制代码
# 删除之前的输出目录(Hadoop不允许输出目录已存在)
[atguigu@hadoop102 ~]$ hadoop fs -rm -r /wordcount/output

# 创建新的输入数据(包含大小写和多余空格,测试我们的自定义程序)
[atguigu@hadoop102 ~]$ vim word2.txt

输入内容:

复制代码
Hadoop HDFS YARN MapReduce
Hadoop  Spark  Flink
hadoop spark HIVE hive
  Java  java  JAVA  
Python scala SQL

上传:

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -put word2.txt /wordcount/input2/

6.2 提交作业

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop jar ~/projects/wordcount/target/wordcount-1.0-SNAPSHOT.jar com.atguigu.mapreduce.WordCountDriver /wordcount/input2 /wordcount/output2

6.3 查看YARN上的作业

浏览器访问:http://hadoop103:8088

可以看到作业运行状态:

  • Application Type: MAPREDUCE
  • State: FINISHED
  • FinalStatus: SUCCEEDED

6.4 查看输出结果

bash 复制代码
[atguigu@hadoop102 ~]$ hadoop fs -cat /wordcount/output2/part-r-00000
flink	1
hadoop	3
hdfs	1
hive	2
java	3
mapreduce	1
python	1
scala	1
spark	2
sql	1
yarn	1

可以看到:

  • 所有单词已转为小写(Javajava
  • 多余空格已被过滤
  • 统计结果正确

七、集群模式 vs 本地模式对比

对比项 本地模式(参考文章) 集群模式(本文)
运行环境 Windows IDEA Linux Hadoop集群
数据存储 本地文件系统 HDFS分布式文件系统
计算资源 单机CPU/内存 多节点分布式计算
提交方式 右键运行main方法 hadoop jar 命令提交
YARN调度 不涉及 ResourceManager统一调度
适用场景 本地开发调试 生产环境运行
扩展性 可扩展至数千节点
容错性 任务失败自动重试

八、常见问题与解决

8.1 输出目录已存在

复制代码
org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory hdfs://hadoop102:8020/wordcount/output already exists

解决:Hadoop不允许输出目录已存在,先删除或更换输出路径:

bash 复制代码
hadoop fs -rm -r /wordcount/output

8.2 权限不足

复制代码
org.apache.hadoop.security.AccessControlException: Permission denied: user=atguigu, access=WRITE, inode="/":root:supergroup:drwxr-xr-x

解决 :在 core-site.xml 中配置静态用户:

xml 复制代码
<property>
    <name>hadoop.http.staticuser.user</name>
    <value>atguigu</value>
</property>

或在HDFS上创建用户目录并授权:

bash 复制代码
hadoop fs -mkdir -p /user/atguigu
hadoop fs -chown atguigu:atguigu /user/atguigu

8.3 Container被杀死

复制代码
Container killed by the ApplicationMaster.
Container exited with a non-zero exit code 143.

解决:通常是内存不足,增大任务内存配置:

bash 复制代码
# 提交时指定内存
hadoop jar wordcount.jar com.atguigu.mapreduce.WordCountDriver \
    -Dmapreduce.map.memory.mb=2048 \
    -Dmapreduce.reduce.memory.mb=4096 \
    /input /output

8.4 数据倾斜

某些Reduce任务处理数据量远大于其他,导致整体延迟。

解决

  • 自定义Partitioner打散热点key
  • 使用Combiner进行Map端预聚合
  • 调整Reduce任务数:job.setNumReduceTasks(10)

九、作业监控与日志查看

9.1 YARN Web UI查看

访问 http://hadoop103:8088,点击完成的作业ID,可以查看:

信息 说明
Application Overview 作业概览,包括启动时间、结束时间、运行状态
ApplicationMaster AM的日志链接
Logs 所有Container的日志聚合
Map Tasks Map任务列表及状态
Reduce Tasks Reduce任务列表及状态

9.2 命令行查看日志

bash 复制代码
# 查看指定application的日志
yarn logs -applicationId application_1714012800000_0001

# 查看特定Container的日志
yarn logs -applicationId application_1714012800000_0001 -containerId container_xxx

9.3 JobHistory查看历史作业

访问 http://hadoop102:19888/jobhistory

可以查看已完成的作业详情,包括:

  • 作业配置参数
  • 每个Map/Reduce任务的耗时
  • 任务失败原因和重试记录

十、总结

本文在3节点完全分布式Hadoop集群上,完成了第一个MapReduce程序------WordCount的开发和运行:

步骤 内容
1 确认集群环境(HDFS + YARN正常运行)
2 准备HDFS输入数据
3 运行官方WordCount示例验证集群
4 自定义Java程序(Mapper + Reducer + Driver)
5 Maven打包并提交到集群运行
6 查看YARN作业监控和输出结果

关键点

  • 集群模式下数据必须存储在HDFS上
  • 输出目录不能提前存在
  • job.setJarByClass() 是集群运行的关键
  • YARN Web UI是排查问题的重要工具

如果这篇文章对你有帮助,欢迎点赞、收藏、评论三连!有问题可以在评论区留言交流~

相关推荐
A hao1 分钟前
高对比度在XR虚拟背景中的作用
大数据·图像处理·人工智能·xr·广告
Haoxuekeji1 小时前
山东 AI 智能批改校园电子阅卷企业
大数据·人工智能·深度学习·安全·ai
酉鬼女又兒2 小时前
零基础入门 DeepSeek V4 Pro API 开发:从环境搭建、消息格式规范到翻译函数实战、少样本提示、多轮对话聊天机器人与常见报错全流程详解指南
大数据·网络·数据库·人工智能·macos·机器人·github
ddshub_cc3 小时前
理解 Loop Engineering 与 AI 编程成本优化
大数据·人工智能·gpt·prompt·ai编程
喵喵喵~~~~3 小时前
2026 指挥控制台源头工厂深度测评:按行业项目落地能力择优采购指南
大数据·运维·人工智能·智能家居
喵喵喵~~~~3 小时前
2026 调度操作台厂商横向实测:多行业落地案例拆解,指挥中心控制台怎么选
大数据·人工智能
hkNaruto4 小时前
【大数据】Demo 城市供水管网智能监控系统——项目开发实施计划
大数据·人工智能
小顿的企业观察4 小时前
从人形机器人到AI销售智能体,AI正在重塑中企出海的底层逻辑
大数据·运维·人工智能·机器人·产品运营·制造
市象5 小时前
卡萨帝高端二十年,难靠冰洗再独秀
大数据·智能家居
Georgeviewer5 小时前
数字化工程落地复盘:通用SaaS架构的本地化缺陷与自研系统最优解|皖禾数智实战
大数据·人工智能·架构