Hadoop学习教程,从入门到精通, 部署Hadoop 3.x — 知识点详解(2)

部署Hadoop 3.x --- 知识点详解


一、安装准备

1.1 硬件与软件需求

项目 最低要求 推荐配置
CPU 2核 4核及以上
内存 4GB 8GB及以上
磁盘 40GB 100GB及以上
操作系统 CentOS 7.x / Ubuntu 18.04 CentOS 7.9
虚拟化软件 VMware Workstation 15+ VMware 16+
JDK JDK 8 JDK 8u202
Hadoop Hadoop 3.1.3 / 3.3.x Hadoop 3.1.3

1.2 软件资源准备

需要提前下载以下软件包:

text 复制代码
# VMware Workstation 安装包
VMware-Workstation-full-16.x.x.exe

# CentOS 7 镜像文件
CentOS-7-x86_64-DVD-2009.iso

# JDK 安装包(注意:需要 Linux 版本的 tar.gz 格式)
jdk-8u202-linux-x64.tar.gz

# Hadoop 安装包
hadoop-3.1.3.tar.gz

1.3 网络规划

在部署之前,需要规划好集群的网络配置:

text 复制代码
# ==================== 集群网络规划表 ====================
#
# 主机名          IP地址              角色
# -------          --------              ----
# bigdata110        192.168.10.110      NameNode / ResourceManager
# bigdata112        192.168.10.112      DataNode / NodeManager
# bigdata113        192.168.10.113      DataNode / NodeManager
#
# 子网掩码:255.255.255.0
# 网关:192.168.10.2
# DNS:8.8.8.8

二、创建虚拟机

2.1 在VMware中新建虚拟机的步骤

步骤一:启动新建向导

text 复制代码
# 1. 打开 VMware Workstation
# 2. 点击菜单栏 "文件" -> "新建虚拟机"
# 3. 选择 "自定义(高级)" -> 点击 "下一步"

步骤二:选择硬件兼容性

text 复制代码
# 硬件兼容性选择 "Workstation 16.x"
# 该选项决定了虚拟机可用的硬件特性版本

步骤三:安装操作系统

text 复制代码
# 选择 "稍后安装操作系统"
# 这样可以先配置硬件,再手动指定ISO镜像

步骤四:选择操作系统类型

text 复制代码
# 客户机操作系统:Linux
# 版本:CentOS 7 64位

步骤五:命名虚拟机并选择存储位置

text 复制代码
# 虚拟机名称:bigdata110
# 安装位置:D:\VirtualMachines\bigdata110
# 建议将虚拟机文件放在非系统盘,路径中不要包含中文

步骤六:处理器配置

text 复制代码
# 处理器数量:2
# 每个处理器的内核数量:2
# 总共分配 2×2=4 个逻辑处理器

步骤七:内存配置

text 复制代码
# 内存大小:4096MB(4GB)
# 如果宿主机内存充足,建议分配 8192MB(8GB)

步骤八:网络类型

text 复制代码
# 选择 "使用网络地址转换(NAT)"
# NAT模式下,虚拟机可以通过宿主机访问外网
# 宿主机和虚拟机之间可以互相通信

步骤九:I/O控制器类型

text 复制代码
# 选择推荐的 "LSI Logic"

步骤十:磁盘类型

text 复制代码
# 选择推荐的 "SCSI"

步骤十一:选择磁盘

text 复制代码
# 选择 "创建新虚拟磁盘"

步骤十二:指定磁盘容量

text 复制代码
# 最大磁盘大小:50GB
# 选择 "将虚拟磁盘存储为单个文件"
# 不要勾选 "立即分配所有磁盘空间"(按需增长节省空间)

步骤十三:完成创建

text 复制代码
# 点击 "完成" 完成虚拟机创建
# 此时虚拟机尚未安装操作系统

2.2 挂载ISO镜像并安装CentOS 7

text 复制代码
# 1. 在 VMware 中选中 bigdata110 虚拟机
# 2. 点击 "编辑虚拟机设置"
# 3. 选择 "CD/DVD (IDE)"
# 4. 右侧选择 "使用ISO映像文件"
# 5. 浏览并选择 CentOS-7-x86_64-DVD-2009.iso
# 6. 点击 "确定"
# 7. 启动虚拟机,开始安装

2.3 CentOS 7 安装过程关键选项

text 复制代码
# ===================== 安装过程关键步骤 =====================

# 步骤1:选择安装语言
# 选择 "English" -> "English (United States)"

# 步骤2:INSTALLATION DESTINATION(安装目标磁盘)
# 点击进入后直接点击 "Done"(使用默认自动分区)

# 步骤3:NETWORK & HOST NAME(网络和主机名)
# 1. 打开右上角的网络开关(OFF -> ON)
# 2. 主机名设置为:bigdata110
# 3. 点击 "Apply" 使主机名生效
# 4. 点击 "Done"

# 步骤4:Begin Installation(开始安装)

# 步骤5:设置ROOT密码
# ROOT密码:123456(学习环境简单密码即可)
# 如果密码过于简单,需要点两次 "Done" 确认

# 步骤6:等待安装完成,点击 "Reboot" 重启

三、配置虚拟机(网络、主机名、映射、防火墙)

3.1 配置虚拟机网络(静态IP)

bash 复制代码
# ==================== 修改网络配置文件 ====================
# 使用 vi 编辑器打开网络配置文件
# 文件路径:/etc/sysconfig/network-scripts/ifcfg-ens33
# ens33 是网卡名称,不同机器可能不同,可用 ip addr 查看

vi /etc/sysconfig/network-scripts/ifcfg-ens33

文件内容修改如下:

bash 复制代码
# ==================== ifcfg-ens33 完整配置 ====================

# 网络类型:以太网
TYPE=Ethernet

# 网卡设备名称
DEVICE=ens33

# 开机自启动:yes 表示开机自动启用该网卡
ONBOOT=yes

# 启动协议:static 表示使用静态IP地址
# 如果是 dhcp 则表示自动获取IP
BOOTPROTO=static

# 设置IP地址(根据自己的网络规划修改)
# 该地址在NAT网段内选择一个未被占用的
IPADDR=192.168.10.110

# 子网掩码:255.255.255.0
NETMASK=255.255.255.0

# 网关地址:与VMware中NAT设置的网关一致
# VMware -> 编辑 -> 虚拟网络编辑器 -> NAT设置 -> 网关IP
GATEWAY=192.168.10.2

# DNS服务器地址:用于域名解析,8.8.8.8 是 Google 公共DNS
DNS1=8.8.8.8

# 备用DNS
DNS2=114.114.114.114

修改完成后需要重启网络服务:

bash 复制代码
# 重启网络服务使配置生效
systemctl restart network

# 验证网络是否配置成功(查看IP地址是否已更改)
ip addr

# 测试是否能访问外网(-c 4 表示只发送4个包)
ping -c 4 www.baidu.com

# 如果 ping 不通,检查网关和DNS配置

3.2 配置主机名

bash 复制代码
# ==================== 查看当前主机名 ====================
hostname
# 输出示例:localhost.localdomain

# ==================== 修改主机名 ====================
# 方法一:使用 hostnamectl 命令(推荐,永久生效)
hostnamectl set-hostname bigdata110

# 方法二:直接编辑 /etc/hostname 文件
vi /etc/hostname
# 将文件内容改为:bigdata110

# 重启后生效
reboot

3.3 配置主机名映射(/etc/hosts)

bash 复制代码
# ==================== 配置主机名与IP的映射关系 ====================
# 作用:让集群中每台机器都能通过主机名访问其他机器

# 编辑 hosts 文件
vi /etc/hosts

# 在文件末尾添加以下内容(每台机器都要添加)
text 复制代码
# ==================== /etc/hosts 文件内容 ====================

# 以下三行是集群中所有节点的映射关系
# 格式:IP地址    主机名
192.168.10.110    bigdata110
192.168.10.112    bigdata112
192.168.10.113    bigdata113
bash 复制代码
# 验证主机名映射是否生效
ping -c 2 bigdata110
ping -c 2 bigdata112
ping -c 2 bigdata113

提示: Windows 电脑上也需要配置 hosts 文件,路径为 C:\Windows\System32\drivers\etc\hosts,添加相同的映射关系,以便在 Windows 浏览器中访问 Hadoop 的 Web UI。

3.4 关闭防火墙

bash 复制代码
# ==================== 关闭防火墙 ====================
# Hadoop 集群各节点之间需要大量端口通信
# 学习环境中建议关闭防火墙,避免通信被阻止

# 查看防火墙状态
systemctl status firewalld

# 关闭防火墙(立即生效,重启后失效)
systemctl stop firewalld

# 禁止防火墙开机自启动(永久生效)
systemctl disable firewalld

# 验证防火墙已关闭
systemctl status firewalld
# 输出应显示 "inactive (dead)"

3.5 创建普通用户并配置 sudo 权限

bash 复制代码
# ==================== 创建 bigdata 用户 ====================
# 创建用户 bigdata,并设置密码为 bigdata
useradd bigdata
passwd bigdata
# 输入两次密码:bigdata

# ==================== 配置 sudo 权限 ====================
# 让 bigdata 用户可以使用 sudo 执行 root 命令
# 编辑 sudoers 文件
vi /etc/sudoers

# 在 root ALL=(ALL) ALL 下面添加一行:
# bigdata  ALL=(ALL)       NOPASSWD:ALL
# 含义:bigdata 用户可以在任何主机上以任何用户身份执行任何命令,且不需要输入密码
text 复制代码
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
bigdata ALL=(ALL)       NOPASSWD:ALL

3.6 在 /opt 目录下创建文件夹

bash 复制代码
# ==================== 创建软件安装和数据目录 ====================
# 切换到 /opt 目录
cd /opt

# 创建 module 目录:用于安装 Hadoop、JDK 等软件
sudo mkdir module

# 创建 software 目录:用于存放安装包(tar.gz 文件)
sudo mkdir software

# 修改目录所有者为 bigdata 用户
# -R 表示递归修改(包括目录下所有子目录和文件)
sudo chown bigdata:bigdata /opt/module
sudo chown bigdata:bigdata /opt/software

四、克隆虚拟机

4.1 关机并克隆

text 复制代码
# ==================== 克隆虚拟机步骤 ====================

# 1. 确保 bigdata110 已完全关机(不是挂起)
#    在终端执行:
shutdown -h now

# 2. 在 VMware 中右键 bigdata110 -> "管理" -> "克隆"

# 3. 克隆向导:
#    - 克隆源:选择 "虚拟机中的当前状态"
#    - 克隆类型:选择 "创建完整克隆"(独立副本,不依赖原始虚拟机)
#    - 虚拟机名称:bigdata112
#    - 存储位置:D:\VirtualMachines\bigdata112
#    - 点击 "完成"

# 4. 重复以上步骤,再克隆一台 bigdata113

4.2 修改克隆虚拟机的配置

对 bigdata112 和 bigdata113分别执行以下操作:

bash 复制代码
# ==================== bigdata112 的修改 ====================

# 1. 修改 IP 地址
vi /etc/sysconfig/network-scripts/ifcfg-ens33
# 将 IPADDR 改为:192.168.10.112

# 2. 修改主机名
hostnamectl set-hostname bigdata112

# 3. 重启生效
reboot
bash 复制代码
# ==================== bigdata113的修改 ====================

# 1. 修改 IP 地址
vi /etc/sysconfig/network-scripts/ifcfg-ens33
# 将 IPADDR 改为:192.168.10.113

# 2. 修改主机名
hostnamectl set-hostname bigdata113

# 3. 重启生效
reboot

4.3 验证克隆虚拟机网络

bash 复制代码
# 在 bigdata112 上验证
# 确认 IP 已更改
ip addr | grep 192.168

# 测试与 bigdata110 的连通性
ping -c 2 bigdata110

# 测试外网连通性
ping -c 2 www.baidu.com

五、安装JDK

5.1 卸载系统自带的 JDK

bash 复制代码
# ==================== 检查并卸载系统自带JDK ====================
# CentOS 7 可能自带 OpenJDK,需要先卸载

# 查询系统已安装的 Java 相关包
rpm -qa | grep -i java

# 如果有输出,逐一卸载(以下命令中的包名根据实际查询结果替换)
# --nodeps 表示不检查依赖关系,强制卸载
sudo rpm -qa | grep -i java | xargs -n1 sudo rpm -e --nodeps

# 再次确认已完全卸载
rpm -qa | grep -i java
# 无输出表示卸载干净

5.2 解压 JDK 安装包

bash 复制代码
# ==================== 解压 JDK 到 /opt/module 目录 ====================
# 进入安装包存放目录
cd /opt/software

# 解压 JDK tar.gz 包到 /opt/module 目录
# -z:通过gzip处理压缩文件
# -x:解压
# -v:显示详细过程
# -f:指定文件名
# -C:指定解压目标目录
tar -zxvf jdk-8u202-linux-x64.tar.gz -C /opt/module/

# 解压完成后,查看解压结果
ls /opt/module/
# 输出:jdk1.8.0_202

5.3 配置 JDK 环境变量

bash 复制代码
# ==================== 配置 JAVA_HOME 环境变量 ====================
# 方法一:编辑 /etc/profile.d/ 目录下创建自定义环境变量脚本(推荐)

# 创建一个专用的环境变量脚本文件
sudo vi /etc/profile.d/my_env.sh

my_env.sh 文件内容:

bash 复制代码
# ==================== /etc/profile.d/my_env.sh ====================

# ---------- JAVA_HOME 环境变量 ----------
# JAVA_HOME 指向 JDK 的安装目录
export JAVA_HOME=/opt/module/jdk1.8.0_202

# 将 JDK 的 bin 目录加入 PATH
# $PATH 表示保留原有的 PATH 值
# 这样在任意目录下都可以直接使用 java、javac 等命令
export PATH=$PATH:$JAVA_HOME/bin
bash 复制代码
# ==================== 让环境变量立即生效 ====================
# source 命令重新加载 profile 文件,使变量无需重启即可生效
source /etc/profile

# 验证 JDK 安装是否成功
# 查看 Java 版本
java -version
# 预期输出:
# java version "1.8.0_202"
# Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
# Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

# 查看 JAVA_HOME 是否正确
echo $JAVA_HOME
# 预期输出:/opt/module/jdk1.8.0_202

5.4 编写 Java 测试程序验证 JDK

bash 复制代码
# ==================== 编写 Hello.java 测试 JDK ====================
# 创建并编辑 Hello.java 文件
vi Hello.java
java 复制代码
// Hello.java - Java 入门测试程序
// 用于验证 JDK 是否正确安装

// 定义一个公共类 Hello,类名必须与文件名一致
public class Hello {
    // main 方法是 Java 程序的入口
    // String[] args 是命令行参数数组
    public static void main(String[] args) {
        // System.out.println() 用于在控制台输出一行文本
        // "Hello, Hadoop!" 是要输出的字符串内容
        System.out.println("Hello, Hadoop!");
    }
}
bash 复制代码
# 编译 Java 源文件(生成 Hello.class 字节码文件)
javac Hello.java

# 运行 Java 程序
java Hello
# 预期输出:Hello, Hadoop!

六、安装Hadoop

6.1 解压 Hadoop 安装包

bash 复制代码
# ==================== 解压 Hadoop 到 /opt/module 目录 ====================
cd /opt/software

# 解压 hadoop-3.1.3.tar.gz 到 /opt/module 目录
tar -zxvf hadoop-3.1.3.tar.gz -C /opt/module/

# 查看解压结果
ls /opt/module/hadoop-3.1.3/
# 输出:bin  etc  include  lib  libexec  LICENSE.txt  NOTICE.txt  
#       README.txt  sbin  share

6.2 配置 Hadoop 环境变量

bash 复制代码
# ==================== 在 my_env.sh 中追加 Hadoop 环境变量 ====================
sudo vi /etc/profile.d/my_env.sh

在已有内容后面追加:

bash 复制代码
# ==================== 追加 HADOOP_HOME 环境变量 ====================

# ---------- HADOOP_HOME 环境变量 ----------
# HADOOP_HOME 指向 Hadoop 的安装目录
export HADOOP_HOME=/opt/module/hadoop-3.1.3

# 将 Hadoop 的 bin 和 sbin 目录都加入 PATH
# bin 目录包含 hadoop、hdfs 等命令
# sbin 目录包含 start-dfs.sh、start-yarn.sh 等启动脚本
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
bash 复制代码
# 重新加载环境变量
source /etc/profile

# 验证 Hadoop 安装
hadoop version
# 预期输出应包含:Hadoop 3.1.3

6.3 Hadoop 目录结构说明

bash 复制代码
# ==================== 查看 Hadoop 安装目录结构 ====================
ls -la /opt/module/hadoop-3.1.3/

# 目录说明:
# bin/        ------ Hadoop 的基本管理脚本和操作命令(hadoop, hdfs, yarn 等)
# etc/        ------ Hadoop 的配置文件目录(core-site.xml, hdfs-site.xml 等)
# include/    ------ C 语言头文件,用于 C/C++ 接口开发
# lib/        ------ Hadoop 的本地库(.so 文件)
# libexec/    ------ Hadoop 的启动/停止脚本内部调用的脚本
# sbin/       ------ Hadoop 的启动和停止脚本(start-dfs.sh, stop-yarn.sh 等)
# share/      ------ Hadoop 的 JAR 包和文档

七、Hadoop 集群部署模式

7.1 三种部署模式对比

text 复制代码
# ==================== Hadoop 三种部署模式 ====================

# | 特性          | 单机模式         | 伪分布式模式           | 完全分布式模式          |
# |--------------|-----------------|----------------------|----------------------|
# | 节点数量      | 1个              | 1个(模拟多个进程)     | 多个(3个及以上)       |
# | 是否使用HDFS  | 否               | 是                    | 是                    |
# | 是否使用YARN  | 否               | 是                    | 是                    |
# | 是否使用守护进程| 否(单JVM运行)   | 是(各组件独立进程)    | 是(分布在不同机器)    |
# | 适用场景      | 调试MapReduce程序 | 开发测试               | 生产环境               |
# | 数据存储      | 本地文件系统       | HDFS(单节点模拟)     | HDFS(多节点分布式)    |

八、基于伪分布式模式部署 Hadoop

8.1 配置 core-site.xml

bash 复制代码
# ==================== 编辑 core-site.xml ====================
# core-site.xml 是 Hadoop 的核心配置文件
# 路径:$HADOOP_HOME/etc/hadoop/core-site.xml
vi /opt/module/hadoop-3.1.3/etc/hadoop/core-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!--
  core-site.xml - Hadoop 核心配置文件
  配置 Hadoop 全局参数,如文件系统地址、临时目录等
-->
<configuration>
    
    <!-- 配置项1:指定 HDFS 中 NameNode 的通信地址 -->
    <!-- 
      property 标签定义一个配置项
      name:配置项名称
      value:配置项的值
      bigdata110 是运行 NameNode 的主机名
      8020 是 HDFS 的 RPC(远程过程调用)端口号
      客户端通过该地址与 NameNode 通信
    -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://bigdata110:8020</value>
    </property>

    <!-- 配置项2:指定 Hadoop 运行时产生文件的存储目录 -->
    <!--
      Hadoop 运行过程中产生的临时文件、NameNode 的元数据等
      会存储在这个目录下
      /opt/module/hadoop-3.1.3/data/tmp 是自定义目录
    -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/module/hadoop-3.1.3/data/tmp</value>
    </property>

    <!-- 配置项3:配置 HDFS 网页登录使用的静态用户 -->
    <!--
      当通过 Web UI 访问 HDFS 时,以哪个用户身份操作
      设置为 bigdata(即我们创建的普通用户)
    -->
    <property>
        <name>hadoop.http.staticuser.user</name>
        <value>bigdata</value>
    </property>

</configuration>

8.2 配置 hdfs-site.xml

bash 复制代码
# ==================== 编辑 hdfs-site.xml ====================
vi /opt/module/hadoop-3.1.3/etc/hadoop/hdfs-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!--
  hdfs-site.xml - HDFS 分布式文件系统配置文件
  配置 HDFS 的副本数量、NameNode/DataNode 数据目录等
-->
<configuration>

    <!-- 配置项1:指定 HDFS 副本数量 -->
    <!--
      在完全分布式模式中,数据默认存储3个副本
      伪分布式模式只有1个节点,所以副本数设为1
      如果设为3但只有1个DataNode,HDFS 会持续警告副本不足
    -->
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>

    <!-- 配置项2:NameNode 的 Web UI 访问端口(伪分布式模式可选配置) -->
    <!--
      默认端口是 9870(Hadoop 3.x)
      Hadoop 2.x 中默认是 50070
      通过浏览器访问:http://bigdata110:9870
    -->
    <property>
        <name>dfs.namenode.http-address</name>
        <value>bigdata110:9870</value>
    </property>

    <!-- 配置项3:SecondaryNameNode 的 Web UI 端口 -->
    <!--
      SecondaryNameNode 辅助 NameNode 进行元数据合并
      通过浏览器访问:http://bigdata110:9868
    -->
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>bigdata110:9868</value>
    </property>

</configuration>

8.3 配置 yarn-site.xml

bash 复制代码
# ==================== 编辑 yarn-site.xml ====================
vi /opt/module/hadoop-3.1.3/etc/hadoop/yarn-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<!--
  yarn-site.xml - YARN 资源调度框架配置文件
  配置 ResourceManager 地址、NodeManager 辅助服务等
-->
<configuration>

    <!-- 配置项1:指定 ResourceManager 的通信地址 -->
    <!--
      ResourceManager 负责集群资源的统一管理和调度
      8032 是 ResourceManager 的 RPC 端口
      NodeManager 通过该地址向 ResourceManager 注册和汇报
    -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>bigdata110</value>
    </property>

    <!-- 配置项2:配置 NodeManager 的辅助服务 -->
    <!--
      mapreduce_shuffle 是 MapReduce 必需的混洗(Shuffle)服务
      它负责将 Map 阶段的输出数据传输到 Reduce 阶段
      如果不配置此项,MapReduce 任务将无法运行
    -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>

    <!-- 配置项3:ResourceManager 的 Web UI 端口 -->
    <!--
      通过浏览器访问:http://bigdata110:8088
      可以查看集群资源使用情况和任务运行状态
    -->
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>bigdata110:8088</value>
    </property>

    <!-- 配置项4:开启日志聚集功能 -->
    <!--
      日志聚集:将各容器(Container)的日志收集到 HDFS 中
      便于统一查看和管理任务日志
    -->
    <property>
        <name>yarn.log-aggregation-enable</name>
        <value>true</value>
    </property>

    <!-- 配置项5:日志保存时间为7天(单位:秒) -->
    <!-- 7天 = 7 × 24 × 60 × 60 = 604800 秒 -->
    <property>
        <name>yarn.log-aggregation.retain-seconds</name>
        <value>604800</value>
    </property>

</configuration>

8.4 配置 mapred-site.xml

bash 复制代码
# ==================== 编辑 mapred-site.xml ====================
vi /opt/module/hadoop-3.1.3/etc/hadoop/mapred-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<!--
  mapred-site.xml - MapReduce 框架配置文件
  配置 MapReduce 运行在 YARN 上
-->
<configuration>

    <!-- 配置项:指定 MapReduce 程序运行在 YARN 上 -->
    <!--
      默认值是 local(本地模式)
      设置为 yarn 表示将 MapReduce 任务提交到 YARN 集群执行
      这是分布式运行 MapReduce 的必要配置
    -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>

    <!-- 配置项2:历史服务器地址 -->
    <!--
      MapReduce 历史服务器用于查看已完成任务的历史信息
      10020 是历史服务器的 RPC 端口
      19888 是历史服务器的 Web UI 端口
    -->
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>bigdata110:10020</value>
    </property>

    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>bigdata110:19888</value>
    </property>

</configuration>

8.5 格式化 NameNode 并启动集群

bash 复制代码
# ==================== 格式化 NameNode ====================
# 注意:格式化操作只需要在第一次安装时执行一次!
# 重复格式化会导致 DataNode 的 clusterID 与 NameNode 不一致
# 从而导致 DataNode 无法注册

# 格式化命令
hdfs namenode -format

# 格式化成功标志:在输出信息中看到以下内容
# Storage directory /opt/module/hadoop-3.1.3/data/tmp/dfs/name 
# has been successfully formatted.

# ==================== 启动 HDFS ====================
# 启动 NameNode 和 DataNode
# 该脚本位于 $HADOOP_HOME/sbin/ 目录下
start-dfs.sh

# 验证 HDFS 是否启动成功(应看到 NameNode、DataNode、SecondaryNameNode 三个进程)
jps
# 预期输出:
# 12345 NameNode
# 12346 DataNode
# 12347 SecondaryNameNode
# 12348 Jps

# ==================== 启动 YARN ====================
# 启动 ResourceManager 和 NodeManager
start-yarn.sh

# 再次验证(应多出 ResourceManager 和 NodeManager 两个进程)
jps
# 预期输出:
# 12345 NameNode
# 12346 DataNode
# 12347 SecondaryNameNode
# 12348 ResourceManager
# 12349 NodeManager
# 12350 Jps

# ==================== Web UI 验证 ====================
# 在 Windows 浏览器中访问以下地址:
# HDFS 管理界面:http://bigdata110:9870
# YARN 管理界面:http://bigdata110:8088

8.6 伪分布式模式下的基本操作测试

bash 复制代码
# ==================== 测试 HDFS 文件操作 ====================

# 1. 在 HDFS 上创建目录(-p 表示递归创建多级目录)
hdfs dfs -mkdir -p /user/bigdata/input

# 2. 将本地文件上传到 HDFS
# 上传 Hadoop 的配置文件作为测试数据
hdfs dfs -put /opt/module/hadoop-3.1.3/etc/hadoop/core-site.xml /user/bigdata/input/

# 3. 查看 HDFS 上的文件
hdfs dfs -ls /user/bigdata/input/
# 预期输出中应能看到 core-site.xml 文件

# 4. 从 HDFS 下载文件到本地
hdfs dfs -get /user/bigdata/input/core-site.xml /opt/module/

# 5. 在 HDFS 上删除文件
hdfs dfs -rm /user/bigdata/input/core-site.xml

8.7 配置 SSH 免密登录

bash 复制代码
# ==================== SSH 免密登录配置 ====================
# 为什么需要免密登录?
# 因为 start-dfs.sh 和 start-yarn.sh 启动脚本
# 会通过 SSH 连接到各个节点启动相应进程
# 如果没有免密登录,每次启动都要手动输入密码

# ---------- 在 bigdata110 上生成密钥对 ----------
# -t rsa:指定密钥类型为 RSA
# -P '':指定密码为空(直接按回车)
# -f:指定密钥文件的保存路径
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa

# ---------- 将公钥分发给自己(伪分布式只有一台机器) ----------
# ssh-copy-id:将本地公钥复制到目标主机的 authorized_keys 文件中
ssh-copy-id bigdata110

# ---------- 测试免密登录 ----------
ssh bigdata110
# 如果不需要输入密码即可登录,则配置成功
# 退出登录
exit

8.8 关闭集群

bash 复制代码
# ==================== 关闭集群 ====================

# 停止 YARN
stop-yarn.sh

# 停止 HDFS
stop-dfs.sh

# 验证所有进程已停止
jps
# 只应看到 Jps 进程

九、基于完全分布式模式部署 Hadoop

9.1 集群规划

text 复制代码
# ==================== 完全分布式集群规划 ====================

# | 服务名称        | bigdata110         | bigdata112         | bigdata113        |
# |----------------|-------------------|-------------------|-------------------|
# | HDFS           | NameNode          |                   |                   |
# | HDFS           | DataNode          | DataNode          | DataNode          |
# | HDFS           | SecondaryNameNode |                   |                   |
# | YARN           | ResourceManager   |                   |                   |
# | YARN           | NodeManager       | NodeManager       | NodeManager       |
# | 历史服务器      | JobHistoryServer  |                   |                   |

# 注意:实际生产环境中 NameNode 和 ResourceManager 
# 建议分开部署在不同节点上,以提高可用性

9.2 在三台机器上同步 JDK 和 Hadoop 安装

bash 复制代码
# ==================== 使用 scp 命令同步安装文件 ====================
# scp(Secure Copy):通过 SSH 在不同主机之间安全复制文件

# ---------- 从 bigdata110 复制 JDK 到 bigdata112 和 bigdata113----------
# 格式:scp -r 源文件 用户名@主机名:目标路径
# -r:递归复制整个目录

# 复制 JDK 到 bigdata112
scp -r /opt/module/jdk1.8.0_202 bigdata@bigdata112:/opt/module/

# 复制 JDK 到 bigdata113
scp -r /opt/module/jdk1.8.0_202 bigdata@bigdata113:/opt/module/

# ---------- 从 bigdata110 复制 Hadoop 到 bigdata112 和 bigdata113----------
scp -r /opt/module/hadoop-3.1.3 bigdata@bigdata112:/opt/module/
scp -r /opt/module/hadoop-3.1.3 bigdata@bigdata113:/opt/module/
bash 复制代码
# ==================== 使用 rsync 同步环境变量脚本 ====================
# rsync 与 scp 的区别:rsync 只同步差异部分,速度更快
# -a:归档模式(保留权限、时间戳等)
# -v:显示详细信息

# 同步环境变量脚本到 bigdata112
rsync -av /etc/profile.d/my_env.sh bigdata@bigdata112:/etc/profile.d/

# 同步环境变量脚本到 bigdata113
rsync -av /etc/profile.d/my_env.sh bigdata@bigdata113:/etc/profile.d/

# 在 bigdata112 和 bigdata113上分别执行以下命令使环境变量生效
# 注意:在 bigdata112 和 bigdata113上分别登录执行
source /etc/profile

9.3 编写集群分发脚本 xsync

bash 复制代码
# ==================== 创建 xsync 集群同步脚本 ====================
# 功能:将文件或目录同步分发到集群中所有节点
# 放置路径:~/bin/xsync(需要确保 ~/bin 在 PATH 中)

mkdir -p ~/bin
vi ~/bin/xsync
bash 复制代码
#!/bin/bash
# ==================== xsync 集群分发脚本 ====================
# 脚本功能:将指定文件/目录同步到集群中所有节点
# 使用方式:xsync 文件名或目录名

# ---------- 1. 判断参数个数 ----------
# $# 表示传入脚本的参数个数
# -ne 表示 not equal(不等于)
# 如果没有传入参数,提示用户并退出
if [ $# -lt 1 ]
then
    echo "请输入要分发的文件或目录名称!"
    exit
fi

# ---------- 2. 遍历集群中所有主机 ----------
# for 循环遍历三台主机的主机名
for host in bigdata110 bigdata112 bigdata113
do
    echo "============ 分发到 $host ============"
    
    # ---------- 3. 遍历所有要分发的参数 ----------
    # $@ 表示所有参数
    for file in $@
    do
        # ---------- 4. 判断文件是否存在 ----------
        # -e 判断文件/目录是否存在
        if [ -e $file ]
        then
            # ---------- 5. 获取文件的父目录 ----------
            # pdir=$(cd -P $(dirname $file); pwd)
            # dirname:获取文件所在目录路径
            # cd -P:进入物理路径(解析软链接)
            # pwd:获取当前工作目录的绝对路径
            pdir=$(cd -P $(dirname $file); pwd)
            
            # ---------- 6. 获取文件名 ----------
            # basename:从完整路径中提取文件名
            fname=$(basename $file)
            
            # ---------- 7. 创建目标目录并分发文件 ----------
            # ssh $host:远程登录到目标主机
            # mkdir -p:递归创建目录
            # rsync -av:归档模式同步文件(只传差异部分)
            ssh $host "mkdir -p $pdir"
            rsync -av $pdir/$fname $host:$pdir
        else
            echo "$file 不存在!"
        fi
    done
done
bash 复制代码
# ==================== 赋予脚本可执行权限 ====================
chmod +x ~/bin/xsync

# 测试脚本:将环境变量脚本分发到所有节点
xsync /etc/profile.d/my_env.sh

9.4 配置完全分布式 core-site.xml

bash 复制代码
# ==================== 完全分布式 core-site.xml ====================
# 在 bigdata110 上修改,然后用 xsync 分发到其他节点
vi /opt/module/hadoop-3.1.3/etc/hadoop/core-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!--
  完全分布式模式 - core-site.xml
  与伪分布式的主要区别:
  - fs.defaultFS 的主机名对应实际的 NameNode 所在节点
-->
<configuration>
    
    <!-- 指定 HDFS NameNode 的地址 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://bigdata110:8020</value>
    </property>

    <!-- 指定 Hadoop 运行时产生文件的存储目录 -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/module/hadoop-3.1.3/data/tmp</value>
    </property>

    <!-- 配置 HDFS 网页登录使用的静态用户 -->
    <property>
        <name>hadoop.http.staticuser.user</name>
        <value>bigdata</value>
    </property>

</configuration>

9.5 配置完全分布式 hdfs-site.xml

bash 复制代码
vi /opt/module/hadoop-3.1.3/etc/hadoop/hdfs-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<!--
  完全分布式模式 - hdfs-site.xml
  与伪分布式的关键区别:
  - 副本数量设为 3(因为有 3 个 DataNode)
  - 指定 SecondaryNameNode 所在节点
-->
<configuration>

    <!-- 副本数量设为 3(与 DataNode 数量一致) -->
    <property>
        <name>dfs.replication</name>
        <value>3</value>
    </property>

    <!-- NameNode 的 Web 访问地址 -->
    <property>
        <name>dfs.namenode.http-address</name>
        <value>bigdata110:9870</value>
    </property>

    <!-- SecondaryNameNode 的 Web 访问地址 -->
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>bigdata113:9868</value>
    </property>

</configuration>

9.6 配置完全分布式 yarn-site.xml

bash 复制代码
vi /opt/module/hadoop-3.1.3/etc/hadoop/yarn-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<!--
  完全分布式模式 - yarn-site.xml
-->
<configuration>

    <!-- ResourceManager 所在节点 -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>bigdata110</value>
    </property>

    <!-- NodeManager 的辅助服务 -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>

    <!-- ResourceManager Web UI 地址 -->
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>bigdata110:8088</value>
    </property>

    <!-- 开启日志聚集 -->
    <property>
        <name>yarn.log-aggregation-enable</name>
        <value>true</value>
    </property>

    <!-- 日志保留时间 7 天 -->
    <property>
        <name>yarn.log-aggregation.retain-seconds</name>
        <value>604800</value>
    </property>

    <!-- NodeManager 可用内存(单位:MB) -->
    <!-- 生产环境根据实际服务器内存配置 -->
    <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>4096</value>
    </property>

    <!-- NodeManager 可用 CPU 核数 -->
    <property>
        <name>yarn.nodemanager.resource.cpu-vcores</name>
        <value>4</value>
    </property>

</configuration>

9.7 配置完全分布式 mapred-site.xml

bash 复制代码
vi /opt/module/hadoop-3.1.3/etc/hadoop/mapred-site.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<!--
  完全分布式模式 - mapred-site.xml
-->
<configuration>

    <!-- MapReduce 运行在 YARN 上 -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>

    <!-- 历史服务器地址 -->
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>bigdata110:10020</value>
    </property>

    <!-- 历史服务器 Web 地址 -->
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>bigdata110:19888</value>
    </property>

</configuration>

9.8 配置 workers 文件

bash 复制代码
# ==================== 配置 workers 文件 ====================
# workers 文件指定集群中所有 DataNode / NodeManager 节点
# 每行一个主机名,不能有空格或空行

vi /opt/module/hadoop-3.1.3/etc/hadoop/workers
text 复制代码
# workers 文件内容(每行一个主机名)
bigdata110
bigdata112
bigdata113

9.9 分发配置文件到所有节点

bash 复制代码
# ==================== 将 Hadoop 配置分发到所有节点 ====================
# 使用之前创建的 xsync 脚本

# 分发整个 Hadoop 目录(包含修改后的配置文件)
xsync /opt/module/hadoop-3.1.3/

# 分发 hosts 文件(确保所有节点的主机映射一致)
sudo xsync /etc/hosts

9.10 SSH 免密登录配置(完全分布式)

bash 复制代码
# ==================== 完全分布式 SSH 免密登录 ====================
# 需要在 bigdata110 上能免密登录 bigdata110、bigdata112、bigdata113
# 因为启动脚本从 bigdata110 发起,通过 SSH 到各节点启动进程

# ---------- 在 bigdata110 上执行 ----------
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
ssh-copy-id bigdata110
ssh-copy-id bigdata112
ssh-copy-id bigdata113

# ---------- 在 bigdata112 上执行 ----------
# 需要登录到 bigdata112 执行(bigdata112 上运行 NodeManager 等)
ssh bigdata112
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
ssh-copy-id bigdata110
ssh-copy-id bigdata112
ssh-copy-id bigdata113
exit

# ---------- 在 bigdata113上执行 ----------
ssh bigdata113
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
ssh-copy-id bigdata110
ssh-copy-id bigdata112
ssh-copy-id bigdata113
exit

9.11 格式化 NameNode 并启动集群

bash 复制代码
# ==================== 格式化 NameNode ====================
# 注意:仅在第一次部署时执行一次!
hdfs namenode -format

# ==================== 启动 HDFS(在 bigdata110 上执行) ====================
start-dfs.sh

# 在三台机器上分别验证进程
# ---------- bigdata110 ----------
jps
# 预期输出:NameNode, DataNode, Jps

# ---------- bigdata112(登录到bigdata112查看) ----------
ssh bigdata112
jps
# 预期输出:DataNode, NodeManager, Jps
exit

# ---------- bigdata113----------
ssh bigdata113
jps
# 预期输出:DataNode, NodeManager, SecondaryNameNode, Jps
exit

# ==================== 启动 YARN(在 bigdata110 上执行) ====================
start-yarn.sh

# 再次在 bigdata110 上验证
jps
# 预期输出:NameNode, DataNode, ResourceManager, NodeManager, Jps

# ==================== 启动历史服务器 ====================
# 手动启动 MapReduce 历史服务器
mapred --daemon start historyserver

# 验证历史服务器
jps
# 应多出 JobHistoryServer 进程

9.12 集群启动/停止脚本

bash 复制代码
# ==================== 一键启动/停止集群脚本 ====================
# 创建自定义的集群启动脚本

vi ~/bin/myhadoop.sh
bash 复制代码
#!/bin/bash
# ==================== myhadoop.sh 集群管理脚本 ====================
# 功能:一键启动或停止 Hadoop 集群
# 使用方式:myhadoop.sh start 或 myhadoop.sh stop

# ---------- 判断参数 ----------
# -z 判断字符串是否为空
if [ $# -lt 1 ]
then
    echo "用法: myhadoop.sh {start|stop}"
    exit
fi

# ---------- 判断用户 ----------
# 只有 bigdata 用户才能执行该脚本
if [ $(whoami) != "bigdata" ]
then
    echo "请使用 bigdata 用户执行此脚本!"
    exit
fi

case $1 in
"start")
    echo "============ 启动 HDFS ============"
    # start-dfs.sh:在所有节点上启动 NameNode、DataNode、SecondaryNameNode
    start-dfs.sh
    
    echo "============ 启动 YARN ============"
    # start-yarn.sh:在所有节点上启动 ResourceManager、NodeManager
    start-yarn.sh
    
    echo "============ 启动历史服务器 ============"
    # JobHistoryServer 负责记录已完成的 MapReduce 任务信息
    ssh bigdata110 "mapred --daemon start historyserver"
;;

"stop")
    echo "============ 停止历史服务器 ============"
    ssh bigdata110 "mapred --daemon stop historyserver"
    
    echo "============ 停止 YARN ============"
    stop-yarn.sh
    
    echo "============ 停止 HDFS ============"
    stop-dfs.sh
;;

*)
    echo "用法: myhadoop.sh {start|stop}"
;;
esac
bash 复制代码
# 赋予执行权限
chmod +x ~/bin/myhadoop.sh

# 使用方式
myhadoop.sh start    # 启动集群
myhadoop.sh stop     # 停止集群

9.13 编写查看集群进程脚本 jpsall

bash 复制代码
# ==================== jpsall 集群进程查看脚本 ====================
vi ~/bin/jpsall
bash 复制代码
#!/bin/bash
# ==================== jpsall 脚本 ====================
# 功能:查看集群中所有节点的 Java 进程
# 使用方式:直接执行 jpsall

# 遍历集群中所有主机
for host in bigdata110 bigdata112 bigdata113
do
    echo "========== $host =========="
    # SSH 远程执行 jps 命令
    # jps:列出目标机器上所有 Java 进程的 PID 和名称
    ssh $host jps
done
bash 复制代码
chmod +x ~/bin/jpsall

# 使用方式
jpsall

十、案例------词频统计(WordCount)

10.1 案例说明

WordCount 是 Hadoop MapReduce 的经典入门案例,其功能是统计文本中每个单词出现的次数。

text 复制代码
# ==================== WordCount 执行流程 ====================

# 输入文本:
# hello hadoop
# hello world
# hello java

# 第一阶段:Map(映射)
# Map函数将每一行文本拆分为单独的单词,并为每个单词标记计数1
# hello -> (hello, 1)
# hadoop -> (hadoop, 1)
# hello -> (hello, 1)
# world -> (world, 1)
# hello -> (hello, 1)
# java -> (java, 1)

# 第二阶段:Shuffle(混洗/洗牌)
# 框架自动将相同的 Key 分组到一起
# (hello, [1, 1, 1])
# (hadoop, [1])
# (world, [1])
# (java, [1])

# 第三阶段:Reduce(归约)
# Reduce 函数将同一 Key 的所有 Value 求和
# hello -> 3
# hadoop -> 1
# world -> 1
# java -> 1

10.2 准备测试数据

bash 复制代码
# ==================== 准备 WordCount 测试输入数据 ====================

# 在本地创建测试文件
vi /opt/module/hadoop-3.1.3/wcinput/word.txt
text 复制代码
# word.txt 文件内容
hello hadoop hello mapreduce
hello yarn hello hdfs
hadoop hdfs yarn mapreduce
hello world java python
hadoop hello hdfs world
mapreduce yarn hello python
java hello hadoop hdfs
hdfs yarn hadoop mapreduce
bash 复制代码
# 在 HDFS 上创建输入目录
hdfs dfs -mkdir -p /user/bigdata/wcinput

# 上传测试数据到 HDFS
hdfs dfs -put /opt/module/hadoop-3.1.3/wcinput/word.txt /user/bigdata/wcinput/

# 验证上传成功
hdfs dfs -ls /user/bigdata/wcinput/

10.3 使用 Hadoop 自带的 WordCount 示例运行

bash 复制代码
# ==================== 运行 Hadoop 自带的 WordCount 案例 ====================
# Hadoop 在安装包中已经自带了 WordCount 的示例 JAR 包

# 运行命令格式:
# hadoop jar <jar文件路径> <主类名> <输入路径> <输出路径>

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar \
wordcount \
/user/bigdata/wcinput \
/user/bigdata/wcoutput
bash 复制代码
# ==================== 查看运行结果 ====================
# 查看输出目录
hdfs dfs -ls /user/bigdata/wcoutput/

# 查看统计结果(结果文件名通常是 part-r-00000)
hdfs dfs -cat /user/bigdata/wcoutput/part-r-00000

预期输出结果:

text 复制代码
hadoop    5
hdfs      5
hello     8
java      2
mapreduce 4
python    2
world     2
yarn      4

10.4 手动编写 WordCount Java 代码

java 复制代码
/**
 * WordCount.java - 手动编写的词频统计 MapReduce 程序
 * 
 * MapReduce 程序包含三个核心组件:
 * 1. Mapper 类:负责将输入数据拆分为 Key-Value 对
 * 2. Reducer 类:负责将相同 Key 的 Value 聚合
 * 3. Driver 类:负责配置和提交 MapReduce 任务
 */

// ==================== 导入必要的包 ====================
// 导入 Java IO 包(用于输入输出操作)
import java.io.IOException;

// 导入 Hadoop IO 包(Hadoop 自定义的序列化类型)
import org.apache.hadoop.io.IntWritable;    // Hadoop 的整数类型(可序列化)
import org.apache.hadoop.io.LongWritable;   // Hadoop 的长整数类型(表示偏移量)
import org.apache.hadoop.io.Text;           // Hadoop 的字符串类型(可序列化)

// 导入 Hadoop MapReduce 包
import org.apache.hadoop.mapreduce.Mapper;   // Mapper 基类
import org.apache.hadoop.mapreduce.Reducer;  // Reducer 基类
import org.apache.hadoop.mapreduce.Job;      // Job 类:描述和提交 MapReduce 任务
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;   // 输入格式类
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; // 输出格式类

// 导入 Hadoop 配置和文件系统包
import org.apache.hadoop.conf.Configuration;  // Hadoop 配置对象
import org.apache.hadoop.fs.Path;             // Hadoop 路径类

public class WordCount {

    /**
     * ==================== Mapper 类 ====================
     * 
     * 泛型参数说明:
     * <LongWritable, Text, Text, IntWritable>
     *   参数1(输入 Key)  :LongWritable - 当前行在文件中的字节偏移量
     *   参数2(输入 Value):Text          - 当前行的文本内容
     *   参数3(输出 Key)  : Text          - 单词(字符串)
     *   参数4(输出 Value):IntWritable   - 计数值 1
     */
    public static class WordCountMapper 
            extends Mapper<LongWritable, Text, Text, IntWritable> {
        
        // 创建 IntWritable 对象,值为 1,表示每个单词出现一次
        // 定义为成员变量,避免在 map 方法中重复创建对象,提高性能
        private IntWritable one = new IntWritable(1);
        
        // 创建 Text 对象,用于存储单词
        private Text word = new Text();

        /**
         * map 方法:对输入的每一行数据调用一次
         * 
         * @param key     当前行的偏移量(行号对应的字节位置)
         * @param value   当前行的文本内容
         * @param context 上下文对象,用于输出 Key-Value 对
         */
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            
            // 获取当前行的文本内容,转换为 Java String 类型
            // value.toString() 将 Hadoop Text 类型转为 Java String
            String line = value.toString();
            
            // 使用空格将一行文本拆分为单词数组
            // split("\\s+") 使用正则表达式匹配一个或多个空白字符
            // 这样可以处理多个空格、Tab 等情况
            String[] words = line.split("\\s+");
            
            // 遍历单词数组,输出每个单词和计数 1
            for (String w : words) {
                // 将 Java String 设置到 Hadoop Text 对象中
                word.set(w);
                
                // 输出一个 Key-Value 对:<单词, 1>
                // context.write() 将数据输出到 Shuffle 阶段
                context.write(word, one);
            }
        }
    }

    /**
     * ==================== Reducer 类 ====================
     * 
     * 泛型参数说明:
     * <Text, IntWritable, Text, IntWritable>
     *   参数1(输入 Key)  :Text        - 单词(来自 Mapper 的输出,经过 Shuffle 分组)
     *   参数2(输入 Value):IntWritable - 计数值列表 [1, 1, 1, ...]
     *   参数3(输出 Key)  : Text        - 单词
     *   参数4(输出 Value):IntWritable - 总计数
     */
    public static class WordCountReducer 
            extends Reducer<Text, IntWritable, Text, IntWritable> {
        
        // 创建 IntWritable 对象,用于存储汇总结果
        private IntWritable result = new IntWritable();

        /**
         * reduce 方法:对每个唯一的 Key 调用一次
         * 
         * @param key     当前单词
         * @param values  该单词对应的所有计数值(Iterable 集合)
         * @param context 上下文对象,用于输出最终结果
         */
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context)
                throws IOException, InterruptedException {
            
            // 初始化计数器为 0
            int sum = 0;
            
            // 遍历该单词的所有计数值,累加求和
            // values 是一个迭代器,包含该 Key 对应的所有 Value
            // 例如:单词 "hello" 对应 values = [1, 1, 1, 1, 1]
            for (IntWritable val : values) {
                // val.get() 获取 IntWritable 的整数值
                sum += val.get();
            }
            
            // 将累加结果设置到 Hadoop IntWritable 对象中
            result.set(sum);
            
            // 输出最终结果:<单词, 总次数>
            context.write(key, result);
        }
    }

    /**
     * ==================== Driver 类(主方法) ====================
     * 
     * Driver 类负责:
     * 1. 获取 Hadoop 配置信息
     * 2. 创建 Job 对象
     * 3. 设置 Mapper 和 Reducer 类
     * 4. 设置输入输出路径
     * 5. 提交任务并等待完成
     */
    public static void main(String[] args) throws Exception {
        
        // 获取 Hadoop 默认配置
        Configuration conf = new Configuration();
        
        // 获取 Job 对象(使用 Hadoop 3.x 新 API)
        Job job = Job.getInstance(conf, "WordCount");
        
        // 设置 Jar 包的主类(告诉 Hadoop 去哪个 JAR 中找 Mapper 和 Reducer)
        job.setJarByClass(WordCount.class);
        
        // 设置 Mapper 类
        job.setMapperClass(WordCountMapper.class);
        
        // 设置 Reducer 类
        job.setReducerClass(WordCountReducer.class);
        
        // 设置 Mapper 输出的 Key 类型
        job.setMapOutputKeyClass(Text.class);
        
        // 设置 Mapper 输出的 Value 类型
        job.setMapOutputValueClass(IntWritable.class);
        
        // 设置最终输出(Reducer 输出)的 Key 类型
        job.setOutputKeyClass(Text.class);
        
        // 设置最终输出(Reducer 输出)的 Value 类型
        job.setOutputValueClass(IntWritable.class);
        
        // 设置输入路径(从命令行参数 args[0] 获取)
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        
        // 设置输出路径(从命令行参数 args[1] 获取)
        // 注意:输出路径必须不存在,否则会报错
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        
        // 提交 Job 并等待执行完成
        // waitForCompletion(true):提交任务并等待完成
        // 返回值:true 表示任务成功,false 表示失败
        boolean result = job.waitForCompletion(true);
        
        // 根据任务结果退出:成功返回 0,失败返回 1
        System.exit(result ? 0 : 1);
    }
}

10.5 编译和打包 WordCount 程序

bash 复制代码
# ==================== 方法一:使用 javac 手动编译 ====================

# 创建输出目录
mkdir -p /opt/module/hadoop-3.1.3/wcoutput/classes

# 编译 Java 文件
# -classpath:指定 Hadoop 的 JAR 包作为编译依赖
# -d:指定编译输出的 class 文件存放目录
javac -classpath $(hadoop classpath) \
      -d /opt/module/hadoop-3.1.3/wcoutput/classes \
      WordCount.java

# 打成 JAR 包
# -C:切换到指定目录后再进行打包
# .:打包该目录下所有文件
# c:创建新的 JAR 文件
# v:显示详细信息
# f:指定 JAR 文件名
cd /opt/module/hadoop-3.1.3/wcoutput/classes
jar -cvf wordcount.jar -C . .

# 运行自定义的 WordCount 程序
hadoop jar wordcount.jar WordCount \
/user/bigdata/wcinput \
/user/bigdata/wcoutput2

# 查看结果
hdfs dfs -cat /user/bigdata/wcoutput2/part-r-00000

10.6 使用本地模式运行 MapReduce 程序(调试用)

java 复制代码
/**
 * WordCountLocalDriver.java - 本地模式运行 MapReduce
 * 
 * 本地模式不需要 Hadoop 集群,直接在单机上运行
 * 适用于开发调试阶段
 */
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCountLocalDriver {
    public static void main(String[] args) throws Exception {
        
        // 获取配置对象
        Configuration conf = new Configuration();
        
        // 设置为本地运行模式(使用本地文件系统,不需要 HDFS)
        // 0. 设置本地文件系统(在 IDE 中运行时使用)
        conf.set("fs.defaultFS", "file:///");
        
        // 获取 Job 对象
        Job job = Job.getInstance(conf);
        
        // 设置 Jar 包主类
        job.setJarByClass(WordCountLocalDriver.class);
        
        // 设置 Mapper 和 Reducer 类(复用上面定义的类)
        job.setMapperClass(WordCount.WordCountMapper.class);
        job.setReducerClass(WordCount.WordCountReducer.class);
        
        // 设置输出类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        
        // 设置本地输入输出路径(注意:使用本地路径,非 HDFS 路径)
        FileInputFormat.setInputPaths(job, new Path("D:/input"));
        FileOutputFormat.setOutputPath(job, new Path("D:/output"));
        
        // 提交任务
        boolean result = job.waitForCompletion(true);
        System.exit(result ? 0 : 1);
    }
}

10.7 MapReduce 程序日志查看

bash 复制代码
# ==================== 查看 MapReduce 任务日志 ====================

# 方法一:在 YARN Web UI 上查看
# 浏览器访问:http://bigdata110:8088
# 点击对应的 Application -> 点击 ApplicationMaster 链接
# 可以看到每个 Map 和 Reduce 任务的详细日志

# 方法二:使用 yarn logs 命令查看
# -applicationId:指定要查看的 Application ID
yarn logs -applicationId application_xxxxxxxxxxxxx_xxxx

# 方法三:查看历史任务(如果启用了历史服务器)
# 浏览器访问:http://bigdata110:19888

十一、Hadoop 配置文件总结

11.1 四类配置文件

text 复制代码
# ==================== Hadoop 配置文件分类 ====================

# | 配置文件              | 作用                          | 是否必须修改 |
# |----------------------|-------------------------------|------------|
# | core-site.xml        | Hadoop 核心配置(文件系统地址等) | 是         |
# | hdfs-site.xml        | HDFS 配置(副本数、目录等)      | 是         |
# | yarn-site.xml        | YARN 配置(ResourceManager等)  | 是         |
# | mapred-site.xml      | MapReduce 配置(运行框架等)     | 是         |
# | workers              | 集群节点列表                    | 是         |
# | hadoop-env.sh        | Hadoop 环境变量(JVM参数等)     | 可选       |

# 配置文件路径:$HADOOP_HOME/etc/hadoop/

11.2 hadoop-env.sh 关键配置

bash 复制代码
# ==================== hadoop-env.sh 关键环境变量 ====================
vi /opt/module/hadoop-3.1.3/etc/hadoop/hadoop-env.sh

# 在文件中找到以下配置项,根据实际情况修改
bash 复制代码
# 指定 JAVA_HOME(Hadoop 3.x 中需要显式指定)
export JAVA_HOME=/opt/module/jdk1.8.0_202

# Hadoop 守护进程的 JVM 堆内存参数(根据服务器内存调整)
# 表示 NameNode/DataNode 等进程最大使用 1024MB 内存
export HADOOP_HEAPSIZE=1024

# HDFS NameNode 的专用堆内存(可单独设置,默认使用 HADOOP_HEAPSIZE)
export HDFS_NAMENODE_OPTS="-Xmx1024m"

# HDFS DataNode 的专用堆内存
export HDFS_DATANODE_OPTS="-Xmx1024m"

十二、常见问题排查

12.1 格式化 NameNode 后 DataNode 无法启动

bash 复制代码
# ==================== 问题原因 ====================
# 重复执行 hdfs namenode -format 后,NameNode 生成了新的 clusterID
# 而 DataNode 中记录的还是旧的 clusterID,导致不匹配

# ==================== 解决方法 ====================
# 方法一:删除所有节点的 data 目录后重新格式化
rm -rf /opt/module/hadoop-3.1.3/data/tmp/dfs/data
rm -rf /opt/module/hadoop-3.1.3/data/tmp/dfs/name
hdfs namenode -format

# 方法二:手动修改 DataNode 的 VERSION 文件中的 clusterID
# 使其与 NameNode 的 clusterID 一致
# VERSION 文件路径:/opt/module/hadoop-3.1.3/data/tmp/dfs/data/current/VERSION

12.2 jps 显示进程但 Web UI 无法访问

bash 复制代码
# ==================== 排查步骤 ====================

# 1. 检查端口是否在监听
netstat -tlnp | grep 9870    # NameNode Web UI 端口
netstat -tlnp | grep 8088    # ResourceManager Web UI 端口

# 2. 检查 hosts 文件映射是否正确(Windows 和 Linux 都要检查)
# Linux: /etc/hosts
# Windows: C:\Windows\System32\drivers\etc\hosts

# 3. 检查防火墙是否已关闭
systemctl status firewalld

# 4. 检查 Hadoop 日志中的错误信息
# 日志路径:$HADOOP_HOME/logs/
cat /opt/module/hadoop-3.1.3/logs/hadoop-bigdata-namenode-bigdata110.log

十三、本章小结

text 复制代码
# ==================== 第2章知识要点总结 ====================

# 1. 安装准备
#    - 规划硬件资源、网络配置、软件版本
#    - 下载 VMware、CentOS 镜像、JDK、Hadoop 安装包

# 2. 创建和克隆虚拟机
#    - 使用 VMware 创建 CentOS 7 虚拟机
#    - 通过克隆快速创建多台虚拟机
#    - 克隆后必须修改 IP 地址和主机名

# 3. 虚拟机基础配置
#    - 配置静态 IP 地址
#    - 设置主机名和 hosts 映射
#    - 关闭防火墙
#    - 创建普通用户并配置 sudo 权限

# 4. JDK 安装
#    - 卸载系统自带 OpenJDK
#    - 解压 JDK 到 /opt/module
#    - 配置 JAVA_HOME 环境变量

# 5. Hadoop 安装
#    - 解压 Hadoop 到 /opt/module
#    - 配置 HADOOP_HOME 环境变量

# 6. 三种部署模式
#    - 单机模式:用于本地调试
#    - 伪分布式模式:单节点模拟完整集群(学习和测试)
#    - 完全分布式模式:多节点真实集群(生产环境)

# 7. 核心配置文件
#    - core-site.xml:fs.defaultFS(NameNode地址)、hadoop.tmp.dir(临时目录)
#    - hdfs-site.xml:dfs.replication(副本数)
#    - yarn-site.xml:ResourceManager 地址、Shuffle 服务
#    - mapred-site.xml:运行框架(YARN)

# 8. 集群管理
#    - start-dfs.sh / stop-dfs.sh:启动/停止 HDFS
#    - start-yarn.sh / stop-yarn.sh:启动/停止 YARN
#    - hdfs namenode -format:格式化 NameNode(仅首次)
#    - jps:查看 Java 进程
#    - SSH 免密登录:集群节点间无密码通信

# 9. WordCount 案例
#    - MapReduce 编程模型:Map -> Shuffle -> Reduce
#    - Mapper:将输入拆分为 <Key, Value> 对
#    - Reducer:对相同 Key 的 Value 进行聚合
#    - Driver:配置和提交 MapReduce 任务

# 10. 关键注意事项
#     - NameNode 只能格式化一次
#     - 配置文件修改后需分发到所有节点
#     - 输出目录在运行前必须不存在
#     - Windows 的 hosts 文件需要同步配置
相关推荐
星恒随风1 小时前
C++ 类和对象入门(三):拷贝构造、赋值运算符重载和深浅拷贝
开发语言·c++·笔记·学习
tedcloud1231 小时前
Understand-Anything部署教程:打造AI代码理解平台
服务器·人工智能·学习·自动化·powerpoint
涛思数据(TDengine)2 小时前
从时序数据库到工业AI:涛思数据参编“人工智能+工业软件”评价规范,推动工业数据标准
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据·工业数据库
CyberwayTech2 小时前
赛博威线上营销费用管理咨询:重构企业电商费用管理体系
大数据·人工智能·it·赛博威·营销费用管理·营销费用管理咨询
逆光的July2 小时前
Logback 学习笔记
笔记·学习·logback
数智工坊2 小时前
周志华《Machine Learning》学习笔记--第十三章--半监督学习
笔记·学习·机器学习
五度易链-区域产业数字化管理平台2 小时前
产业大脑技术架构拆解:从多源数据治理到产业链断点识别的全链路实现逻辑
大数据
AI_零食2 小时前
鸿蒙原生 ArkTS:margin 溢出、Row 弹性分配与 alignItems 的交互
学习·华为·开源·harmonyos·鸿蒙·鸿蒙系统
AOwhisky2 小时前
MySQL 学习笔记(第七期):高可用架构进阶与综合项目实战
linux·运维·笔记·学习·mysql·高可用·mha