[Hadoop]三、Hadoop-MapReduce

1. MapReduce概述

1.1 MapReduce定义

MapReduce是一个分布式计算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。MapReduce核心功能是将用户编写的业务逻辑代码自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。

将数据处理任务分解为两个独立的步骤:Map 和 Reduce。

        Map 阶段:在这个阶段,数据被拆分成若干独立的片段,每个片段被应用相同的处理操作。Map 阶段的输出是一个键-值对的集合。

        Reduce 阶段:Reduce 阶段接受 Map 阶段输出的键-值对集合,并将具有相同键的值聚合在一起。这些值可以来自于不同的 Map 任务的输出。

MapReduce 框架自动处理并发、错误恢复、数据分发和负载平衡等问题,使得开发者可以专注于编写简单的 Map 和 Reduce 函数,而不必担心分布式系统的复杂性。这种模型适用于大规模数据集的并行处理,例如在分布式系统中处理海量的结构化或非结构化数据。

1.2 MapReduce优缺点

1.2.1 优点

1MapReduce易于编程

它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。

2)良好的扩展性

当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。

3)高容错性

MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。

4)适合PB级以上海量数据的离线处理

可以实现上千台服务器集群并发工作,提供海量数据处理能力。

1.2.2 缺点

1)不擅长实时计算

MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。

2)不擅长流式计算

流式计算的输入数据是动态的,而MapReduce输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。

3)不擅长DAG(有向无环图)计算

多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。

1.3 MapReduce核心思想

(1)分布式的运算程序往往需要分成至少2个阶段。

(2)第一个阶段的MapTask并发实例,完全并行运行,互不相干。

(3)第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。

(4)MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。

1.4 MapReduce进程

一个完整的MapReduce程序在分布式运行时有三类实例进程:

(1)MrAppMaster:负责整个程序的过程调度及状态协调。

(2)MapTask:负责Map阶段的整个数据处理流程。

(3)ReduceTask:负责Reduce阶段的整个数据处理流程。

1.5 常用数据序列化类型

Java类型

Hadoop Writable类型

Boolean

BooleanWritable

Byte

ByteWritable

Int

IntWritable

Float

FloatWritable

Long

LongWritable

Double

DoubleWritable

String

Text

Map

MapWritable

Array

ArrayWritable

Null

NullWritable

1.6 MapReduce编程规范

用户编写的程序分成三个部分:Mapper、Reducer和Driver。

1. Mapper阶段

        (1)用户自定义的Mapper要继承父类(Mapper<LongWritable, Text, Text, IntWritable>)

        (2)Mapper的输入数据是K-V键值对的形式(KV的类型可以自定义)

        (3)Mapper中的业务逻辑要写作map()方案中

        (4)Mapper的输出数据是K-V键值对的形式(KV的类型自定义)

        (5)map()方法(Map Task进程)对每一个K-V调用一次

2. Reducer阶段

        (1)用户自定义的Reducer类要继承Reducer父类

        (2)Reducer的输入数据类型对应Mapper的输出数据类型,也是K-V

        (3)Reducer的业务逻辑要写在reduce() 方法中

        (4)ReduceTask进程对每一组相同的K-V调用一次reduce() 方法

3. Driver阶段

        相当于Yarn集群的客户端,用于提交整个程序到Yarn集群,提交的是封装了MapReduce程序相关运行参数的job对象。

1.7 WordCount案例实操

1.7.1 本地测试

1)需求:在给定的文本文件中统计输出每一个单词出现的总次数

        (1)输入数据:test.txt

cls cls
java hadoop
hadoop hive spark
hive spark java
hadoop scala flink

        (2)期望输出数据

                cls 2

                flink 1

                hadoop 3

                hive 2

                scala 1

                spark 2

2)需求分析:按照MapReduce编程规范,分别编写Mapper,Reducer,Driver。

        (1)输入数据

        (2)输出数据

        (3)Mapper

                a. 将文本内容先转换成String

                b. 根据空格将一行内容切分成单词

                c. 将单词输出为<单词, 1>

        (4)Reducer

                a. 汇总各个key的个数

                b. 输出该key的总次数

        (5)Driver

a.获取配置信息,获取job对象实例
b.指定本程序的jar包所在的本地路径
c.关联Mapper/Reducer业务类
d.指定Mapper输出的kv类型
e.指定最终输出的数据的kv类型
f.指定job的输入原始文件所在的目录
g.指定job的输出结果所在的目录
h.提交作业

3)环境准备

        (1)创建maven工程 MapReduceDemo

                修改idea配置,使用自己安装的maven程序

                

                创建maven 工程

                

        (2)在pom.xml文件中添加如下依赖。

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.30</version>
    </dependency>
</dependencies>

        (3)在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入。

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

        (4)创建包名:com.hwcloud.mapreduce.wordcount

4)编程程序

        (1)编写Mapper类

package com.hwcloud.mapreduce.wordcount;


import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * ClassName:    WordCountMapper
 * Package:      com.hwcloud.mapreduce.wordcount
 *
 * @Author: Seven
 * @Create: 2024/6/10 10:40
 * @Version: 1.0
 * @Description: 简单的Hadoop WordCount MR任务的实现
 */
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    Text k = new Text();
    IntWritable v = new IntWritable(1);

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        // 1.获取一行数据
        String line = value.toString();
        // 2.切割
        String[] words = line.split(" ");
        // 3.输出
        for (String word : words) {
            k.set(word);
            context.write(k, v);
        }
    }
}

        (2)编写Reducer类

package com.hwcloud.mapreduce.wordcount;

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

import java.io.IOException;

/**
 * ClassName:    WordCountReducer
 * Package:      com.hwcloud.mapreduce.wordcount
 *
 * @Author: Seven
 * @Create: 2024/6/10 10:56
 * @Version:
 * @Description:
 */
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    int sum;
    IntWritable v = new IntWritable();

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {

        // 1 累加求和
        sum = 0;
        for (IntWritable count : values) {
            sum += count.get();
        }

        // 2 输出
        v.set(sum);
        context.write(key, v);
    }
}

        (3)编写Driver类

package com.hwcloud.mapreduce.wordcount;

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;

import java.io.IOException;

/**
 * ClassName:    WordCountDriver
 * Package:      com.hwcloud.mapreduce.wordcount
 *
 * @Author: Seven
 * @Create: 2024/6/10 10:58
 * @Version:
 * @Description:
 */
public class WordCountDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        // 1 获取配置信息以及获取job对象
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        // 2 关联本Driver程序的jar
        job.setJarByClass(WordCountDriver.class);

        // 3 关联Mapper和Reducer的jar
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        // 4 设置Mapper输出的kv类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        // 5 设置最终输出kv类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // 6 设置输入和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 7 提交job
        boolean result = job.waitForCompletion(true);
        System.exit(result ? 0 : 1);
    }
}

5)本地测试

        (1)需要配置好HADOOP_HOME变量以及Windows运行依赖

        (2)在IDEA中运行,输入必要的参数

        

        (3)运行结果说明

                

                前面两个文件是校验文件,_SUCCESS 运行成功标识,大小为0,part-r-00000 结果输出文件:r 表示reduce端,如果没有reduce就没有r, 00000 表示第一个分区,如果有多个分区,依次递增,其内容如下。(在 MapReduce 任务中运行 WordCount 程序时出现一个额外的空行通常是由于输出文本文件的最后一行末尾有一个换行符导致的。这是因为大多数文本编辑器要求文件以换行符结尾,而最后一行通常也需要以换行符结束。

                

       注意:输出路径的叶子节点(output)必须是不存在的,否则会报错。

        

1.7.2 提交到集群测试

1)用maven打jar包,添加打包插件的依赖

    <build>
        <plugins>
            <plugin>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.7.1</version>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

说明:maven-compiler-plugin 是maven的打包插件,maven-assembly-plugin是打包的时候可以带上依赖的插件。

2)将程序打成jar包

3)修改不带依赖的jar包名称为wc.jar,并拷贝到Hadoop集群上,放到hadoop的classpath路径下

        sudo yum install lrzsz  (安装rz/sz 命令)

        

4)启动Hadoop集群(HDFS,Yarn),并将测试文件上传到HDFS

        

5)执行WordCount程序:hadoop jar wc.jar com.hwcloud.mapreduce.wordcount.WordCountDriver /input /output

复制全类名:

        

        

2. Hadoop序列化

2.1 序列化概述

1)什么是序列化

序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输。

反序列化就是将收到字节序列(或其他数据传输协议)或者是磁盘的持久化数据,转换成内存中的对象。

2)为什么要序列化

一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。

3)为什么不用Java的序列化

Java的序列化是一个重量级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,Header,继承体系等),不便于在网络中高效传输。所以,Hadoop自己开发了一套序列化机制(Writable)。

4)Hadoop序列化特点:

1)紧凑 :高效使用存储空间。

2)快速:读写数据的额外开销小。

3)互操作:支持多语言的交互

2.2 自定义bean对象实现序列化接口(Writable

在企业开发中往往常用的基本序列化类型不能满足所有需求,比如在Hadoop框架内部传递一个bean对象,那么该对象就需要实现序列化接口。

具体实现bean对象序列化步骤如下7步。

(1)必须实现Writable接口

(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造

public FlowBean() {

        super();

}

(3)重写序列化方法

@Override
public void write(DataOutput out) throws IOException {

        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);

}

(4)重写反序列化方法

@Override
public void readFields(DataInput in) throws IOException {

        upFlow = in.readLong();
        downFlow = in.readLong();
        sumFlow = in.readLong();

}

(5)注意反序列化的顺序和序列化的顺序完全一致

(6)要想把结果显示在文件中,需要重写toString(),可用"\t"分开,方便后续用。

7)如果需要将自定义的bean放在key中传输,则还需要实现Comparable接口,因为MapReduce框中的Shuffle过程要求对key必须能排序。详见后面排序案例。

@Override
public int compareTo(FlowBean o) {

        // 倒序排列,从大到小
        return this.sumFlow > o.getSumFlow() ? -1 : 1;
}

2.3 序列化案例实操        

1)需求:统计每一个手机号耗费的总上行流量、总下行流量、总流量

(1)输入数据:【phone_data.txt】

<think>好的,我现在需要解决用户在运行Hadoop 3.1.3的WordCount示例时遇到的JAR文件缺失的问题。首先,我得回想一下Hadoop的常规安装和示例JAR的位置。通常,Hadoop安装后会自带一些示例JAR,比如hadoop-mapreduce-examples-X.X.X.jar,里面应该包含WordCount等示例程序。 用户提到的是Hadoop 3.1.3版本,可能的问题有几个方面:安装不完整、JAR文件路径错误、或者用户没有正确指定JAR路径。我需要先确认默认情况下这个JAR的位置在哪里。一般来说,Hadoop的示例JAR通常存放在$HADOOP_HOME/share/hadoop/mapreduce目录下,文件名类似hadoop-mapreduce-examples-3.1.3.jar。用户可能没有正确找到这个路径,或者安装过程中该文件未被正确下载或解压。 接下来,用户可能没有正确使用hadoop命令来运行示例。正确的命令应该是通过hadoop jar指定JAR文件,然后传入主类名和输入输出路径。例如:hadoop jar hadoop-mapreduce-examples-3.1.3.jar wordcount /input /output。如果用户没有正确指定JAR路径,或者当前工作目录不在该JAR所在的目录下,就会报错找不到文件。 另外,用户可能误用了旧版本的示例JAR名称。在Hadoop的不同版本中,示例JAR的命名可能有所变化,比如有的版本可能分为多个不同的JAR,而3.1.3可能整合到了一个文件中。需要确认具体的文件名是否存在,或者是否被移动到了其他目录下。 还有一种情况是,用户可能通过源码编译安装Hadoop,但在编译过程中没有生成示例JAR。这时候需要重新编译并确保启用了示例程序的构建选项。或者,用户可能下载的是二进制分发包,但下载过程中文件损坏或不完整,导致示例JAR缺失,这时候需要重新下载安装包并验证完整性。 此外,环境变量HADOOP_HOME是否设置正确也会影响路径的查找。如果用户没有正确设置该变量,可能导致无法定位到JAR文件的位置。需要检查环境变量,并确保命令中使用的路径是正确的绝对路径或相对路径。 最后,如果确实找不到JAR文件,可能需要手动下载示例JAR,或者从源码编译生成。另外,用户也可以考虑使用其他方式运行WordCount,比如直接编写Java代码并打包成JAR,但这可能对于初学者来说比较复杂。 总结可能的解决步骤:检查JAR文件是否存在指定路径,确认命令的正确性,验证Hadoop安装完整性,重新下载或编译示例JAR,检查环境变量设置,以及考虑替代方法。</think>针对Hadoop 3.1.3运行WordCount示例时JAR文件缺失的问题,以下是分步解决方案: ### 1. 确认默认JAR文件路径 Hadoop示例JAR通常位于安装目录的`share/hadoop/mapreduce`子目录中,完整路径为: ```bash $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar ``` 若未设置`$HADOOP_HOME`环境变量,需通过绝对路径访问,例如: ```bash /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar ``` ### 2. 验证文件存在性 执行以下命令检查文件是否存在: ```bash ls $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar ``` 若输出包含`3.1.3`版本号则文件存在,否则需进行以下操作。 ### 3. 重新安装Hadoop示例包 若文件缺失,可能是安装包不完整导致。需: - 从Apache镜像站重新下载二进制包: ```bash wget https://archive.apache.org/dist/hadoop/common/hadoop-3.1.3/hadoop-3.1.3.tar.gz ``` - 解压后验证文件完整性: ```bash tar -tf hadoop-3.1.3.tar.gz | grep "mapreduce/hadoop-mapreduce-examples" ``` ### 4. 通过源码编译生成(可选) 若使用源码编译安装: ```bash mvn clean package -DskipTests -Pdist,native ``` 编译后示例JAR位于: ```bash hadoop-mapreduce-project/target/hadoop-mapreduce-examples-3.1.3.jar ``` ### 5. 执行WordCount的正确命令格式 使用完整路径运行: ```bash hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /input_path /output_path ``` ### 6. 常见错误处理 - **错误提示**:`No such file or class on classpath: wordcount` 原因:未正确指定主类名,应使用`wordcount`全小写格式[^1] - **错误提示**:`Exception in thread "main" java.lang.ClassNotFoundException` 原因:JAR文件路径错误或文件损坏,需重新下载 ### 替代方案 若仍无法解决,可手动创建WordCount程序: ```java // 编译后打包为wc.jar hadoop jar wc.jar WordCount /input /output ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值