(超详细)大数据Hadoop之MapReduce组件

一、MapReduce 简介

1.1 MapReduce的概述

在Hadoop生态圈中,MapReduce属于核心,负责进行分布式计算。
请添加图片描述

MapReduce 核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的 分布式运算程序,并发运行在一个 Hadoop 集群上。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TTUyvkEh-1640235355989)(C:\Users\86157\Desktop\javatest\大数据开发\Hadoop框架\Hadoop生态圈.png)]

1.2 MapReduce的优点

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

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

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

4)适合 PB 级以上海量数据的离线处理 可以实现上千台服务器集群并发工作,提供数据处理能力。

1.3 MapReduce的缺点

1)不擅长实时计算 尚硅谷大数据技术之 Hadoop(MapReduce) MapReduce 无法像 MySQL 一样,在毫秒或者秒级内返回结果。

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

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

1.4 MapReduce核心思想

请添加图片描述

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

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

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

(4)MapReduce 编程模型只能包含一个 Map 阶段和一个 Reduce 阶段,如果用户的业 务逻辑非常复杂,那就只能多个 MapReduce 程序,串行运行。 总结:分析 WordCount 数据流走向深入理解 MapReduce 核心思想

环形缓冲区将在溢写到80%的时候就开始写入分区,目的提高环形缓冲区的利用率,使其一直处于高速运转。

二、MapReduce代码

2.1 MapReduce 进程

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

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

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

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

2.2 MapReduce 编程规范

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

1.Mapper阶段

(1)用户自定义的Mapper要继承自己的父类
(2)Mapper的输入数据是KV对的形式(KV的类型可自定义)
(3)Mapper中的业务逻辑写在map()方法中
(4)Mapper的输出数据是KV对的形式(KV的类型可自定义)
(5)map()方法(MapTask进程)对每一个调用一次

2.Reducer阶段
(1)用户自定义的Reducer要继承自己的父类
(2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
(3)Reducer的业务逻辑写在reduce()方法中
(4)ReduceTask进程对每一组相同k的组调用一次reduce()方法

3.Driver阶段
当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是 封装了MapReduce程序相关运行参数的job对象

三、WordCount 案例实操

3.1 需求

输入数据:hello.txt

输出数据:atguigu 2 banzhang 1 cls 2 hadoop 1 jiao 1 ss 2 xue 1

按照 MapReduce 编程规范,分别编写 MapperReducerDriver

3.2 环境准备

(1)创建maven工程,MapReduceDemo

(2)在pom.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>org.example</groupId>
    <artifactId>MapReduceDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>15</maven.compiler.source>
        <maven.compiler.target>15</maven.compiler.target>
    </properties>
    <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>
   
</project>

在项目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 
3.3 编写程序

新建项目word1目录

WordCountMapper.java

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;
/**
 * KEYIN, Mapper阶段输入的key的类型:Text
 * VALUEIN,Mapper阶段输入value类型:IntWritable
 * KEYOUT,Mapper阶段输出的Key类型:Text
 * VALUEOUT,Mapper阶段输出的value类型:IntWritable
 */
public class WordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
   
    protected Text outK = new Text();
    protected IntWritable outV = 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){
   
            outK.set(word);
            context.write(outK,outV);
        }
    }
}

WordCountReducer.java类

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 org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * KEYIN, reduce阶段输入的key的类型:Text
 * VALUEIN,reduce阶段输入value类型:IntWritable
 * KEYOUT,reduce阶段输出的Key类型:Text
 * VALUEOUT,reduce阶段输出的value类型:IntWritable
 */

public class WordCountReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
   
    private IntWritable outV = new IntWritable();
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
   
        int sum = 0;
        //atguigu(1,1)
        for(IntWritable value : values){
   
            sum += value.get();
        }
        outV.set(sum);
        context.write(key,outV);
    }
}

WordCountDriver类

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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;

import java.io.IOException;

public class WordCountDriver {
   
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
   
        // 1.获取job
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        // 2.设置jar包路径
        job.setJarByClass(WordCountDriver.class);
        // 3.关联mapper和reduce
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);
        // 4.设置map输出的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("D:\\input"));
        FileOutputFormat.setOutputPath(job, new Path("D:\\outputess"));
        // 7.提交job
        boolean result = job.waitForCompletion(true);
        System.exit(result ? 0 : 1);
    }
}

这个是就是我们WordCount案例的全部代码

运行和我们需要的结果一样

3.4 提交到集群测试上
 <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>15</source>
                    <target>15</target>
                </configuration>
            </plugin>
            <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <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>

将这些代码加入到我们的配置文件中

在这里插入图片描述

配置成功后会在此目录下生产这个文件

wc.jar

在hadoop集群上启动WordCount程序

试着启动他,输入:

hadoop jar MapReduceDemo-1.0-SNAPSHOT.jar mapreduce.word2.WordCountDriver /sanguo /outputss

运行成功,其实这之间出现了很多bug我在之前的博客中已经有提到感兴趣的可以去看一下

3.5Hadoop 序列化
1) 序列化

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

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

2) 为什么要序列化

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

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

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

4)序列化逻辑

1)要自己写一个继承Writable

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

//空参构造
public FlowBan() {
   
}

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 {
   
    this.upFlow = in.readLong();
    this.downFlow = in.readLong();
    this.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; } 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-19xEkDgT-1640235355991)(C:\Users\86157\AppData\Local\Temp\1640228602390.png)]

5)编写业务代码

编写一个Bean对象

import org.apache.hadoop.io.Writable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * 1、定义类实现writable接口
 * 2、重写序列化和反序列化方法
 * 3、重写空参构造
 * 4、toString方法
 */
public class FlowBan implements Writable {
   
    private Long upFlow;
    private Long downFlow;
    private long sumFlow; // 总流量

    public Long getUpFlow() {
   
        return upFlow;
    }

    public void setUpFlow(Long upFlow) {
   
        this.upFlow = upFlow;
    }

    public Long getDownFlow() {
   
        return downFlow;
    }

    public void setDownFlow(Long downFlow) {
   
        this.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值