【集群模式】第一个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是排查问题的重要工具

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

相关推荐
User_芊芊君子1 小时前
数据库选型指南:架构演进的技术实践
大数据·数据库·架构
互联网推荐官1 小时前
上海小程序开发的接口安全与数据通信设计:工程实践中的关键决策
大数据·人工智能·物联网·软件工程
pingao14137812 小时前
智联未来:4G温湿度传感器如何重塑数据监测新生.态
大数据·网络·人工智能
数新网络14 小时前
告别“数据沼泽”,拥抱“活水湖”:数新智能基于CyberEngine与Apache Paimon的新一代数据湖仓架构
大数据
实习僧企业版15 小时前
如何为中小企业点亮校招吸引力的灯塔
大数据·春招·雇主品牌·招聘技巧·口碑
塔能物联运维15 小时前
高密度机柜满载怎么办?热管理的“最后一厘米”:两相液冷
大数据
王苏安说钢材A16 小时前
无锡佳钛合不锈钢有限公司三通的焊接工艺
大数据
跨境卫士-小汪17 小时前
旺季前成本项变多跨境卖家如何设定更稳的备货优先级
大数据·人工智能·产品运营·跨境电商·亚马逊
地球资源数据云18 小时前
1951-2025年中国逐年1千米逐月总降水量区域统计数据集_年表_县
大数据·数据结构·数据库·数据仓库·人工智能