大数据学习

HDFS

在这里插入图片描述

NameNode(nn)

就是Master,它是一个主管、管理者,维护元数据信息。

注:元数据Metadata),又称中介数据中继数据,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。

(1)管理HDFS的名称空间;
(2)配置副本策略;
(3)管理数据块(Block)映射信息;
(4)处理客户端读写请求。

DataNode

就是Slave。NameNode下达命令,DataNode执行实际的操作。
(1)存储实际的数据块;
(2)执行数据块的读/写操作。

Client:

就是客户端

(1)文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
(2)与NameNode交互,获取文件的位置信息;
(3)与DataNode交互,读取或者写入数据;
(4)Client提供一些命令来管理HDFS,比如NameNode格式化;
(5)Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作;

Secondary NameNode:

并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
(1)辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode ;
(2)在紧急情况下,可辅助恢复NameNode。

HDFS读写数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fryEvGJP-1635324381464)(HDFS学习.assets/image-20210707165756018.png)]

上图左下角packet是从系统中流最小传输单位,一个单位为64k,先由小packet(由chunk和chunksum组成,后者是校验位)传输到等待队列中(每个小packet516byte),队列攒够64k再进行流传输

(1)客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件,NameNode 检查目标文件是否已存在,父目录是否存在。
(2)NameNode 返回是否可以上传。
(3)客户端请求第一个 Block 上传到哪几个 DataNode 服务器上。
(4)NameNode 返回 3 个 DataNode 节点,分别为 dn1、dn2、dn3。
(5)客户端通过 FSDataOutputStream 模块请求 dn1 上传数据,dn1 收到请求会继续调用dn2,然后 dn2 调用 dn3,将这个通信管道建立完成。
(6)dn1、dn2、dn3 逐级应答客户端。
(7)客户端开始往 dn1 上传第一个 Block(先从磁盘读取数据放到一个本地内存缓存),以 Packet 为单位,dn1 收到一个 Packet 就会传给 dn2,dn2 传给 dn3;dn1 每传一个 packet会放入一个应答队列等待应答。
(8)当一个 Block 传输完成之后,客户端再次请求 NameNode 上传第二个 Block 的服务器。(重复执行 3-7 步)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIA73Oqv-1635324381466)(HDFS学习.assets/image-20210707183619270.png)]

(1)客户端通过 DistributedFileSystem 向 NameNode 请求下载文件,NameNode 通过查
询元数据,找到文件块 所在的 DataNode 地址。
(2)挑选一台 DataNode(就近原则,如果负载过大则随机)服务器,请求读取数据。
(3)DataNode 开始传输数据给客户端(从磁盘里面读取数据输入流,以 Packet 为单位
来做校验)。
(4)客户端以 Packet 为单位接收,先在本地缓存,然后写入目标文件。

NameNode工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZxVXxEz-1635324381471)(HDFS学习.assets/image-20210707185526676.png)]

注:fsimage是存储的数据,即镜像文件,edits是对该数据追加的操作,即编辑日志

注:镜像文件存储在/opt/module/hadoop-3.1.3/data/dfs/name/current

1 ) 第一 阶段:NameNode

(1)第一次启动 NameNode 格式化后,创建 Fsimage 和 Edits 文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode 记录操作日志,更新滚动日志。
(4)NameNode 在内存中对元数据进行增删改。

2 ) 第二 阶段:Secondary NameNode 工作

(1)Secondary NameNode 询问 NameNode 是否需要 CheckPoint。直接带回 NameNode是否检查结果。
(2)Secondary NameNode 请求执行 CheckPoint。
(3)NameNode 滚动正在写的 Edits 日志。
(4)将滚动前的编辑日志和镜像文件拷贝到 Secondary NameNode。
(5)Secondary NameNode 加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件 fsimage.chkpoint。
(7)拷贝 fsimage.chkpoint 到 NameNode。
(8)NameNode 将 fsimage.chkpoint 重新命名成 fsimage。

DateNode工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v32gF45w-1635324381476)(HDFS学习.assets/image-20210707193023765.png)]

(1)一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode 启动后向 NameNode 注册,通过后,周期性(6 小时)的向 NameNode 上报所有的块信息。

(3)心跳每3秒一次,心跳返回结果带有NameNode给该DataNode的命令。

(4)超过10分钟+30秒没有收到DataNode2的心跳,则认为该节点不可用。

面试题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bwgHAmKw-1635324381478)(HDFS学习.assets/image-20210707195519131.png)]

针对第一问,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZp2xTKx-1635324381479)(HDFS学习.assets/image-20210707195616756.png)]

MapReduce

MapReduce核心思想

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBx1jFuV-1635324563407)(MapReduce.assets/image-20210708171252373.png)]

MapReduce优缺点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kd70WRYS-1635324563408)(MapReduce.assets/image-20210707200734068.png)]

MapReduce进程

(1)MrAppMaster:负责整个程序的过程调度及状态协调。(注:又称为Job或mr,后面写Driver类时一开始便是获取Job对象,开始整个序列化操作

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

准备工作

(1)创建 maven 工程,MapReduceDemo

(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>

(2)在项目的 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)创建包名:com.atguigu.mapreduce.wordcount

MapReduce

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

2.Reducer阶段
(2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
(3)Reducer的业务逻辑写在reduce()方法中
(4)ReduceTask进程对每一组相同k的<k,v>组调用一次reduce()方法
3.Driver阶段
相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是
封装了MapReduce程序相关运行参数的job对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F9M46WPY-1635324563410)(MapReduce.assets/image-20210708151316439.png)]

Mapper中接收到的k是在当前行的index值,v是当前行的文本;

Mapper返回值的k是单个单词,v是1;

Reduce中接受到的k是去重合并之后单词,v是类似[1,1,…,1]这样的一个list;

Reduce中返回的k是去重合并之后单词,v是各个单词对应的个数

WordCount思路

Mapper:获取一行数据–》切割–》输出

Reduce:累加求和–》输出

Driver:

1 获取配置信息以及获取 job 对象;

2 关联本 Driver 程序的 jar

3 关联 Mapper 和 Reducer 的 jar

4 设置 Mapper 输出的 kv 类型

5 设置最终输出 kv 类型

6 设置输入和输出路径

7 提交 job

注:log4j报错可在main函数添加BasicConfigurator.configure();

注:在写继承mapper类的子类时,Mapper类是一个泛型类,其中Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>表示这四个参数在实现或继承该类时才确定的类型,如最后一个参数VALUEOUT,在wordcount类中是IntWritable类型,而在FlowMapper中是FlowBean类型,如下所示。

public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean>
{}
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
	public abstract class Context
    implements MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  }
}

序列化(Writable)案例

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmIQKhsG-1635324563415)(MapReduce.assets/image-20210708205107581.png)]

思路

FlowBean:

重写write和readField方法,注意写和读的顺序即序列化和反序列化时的数据读取顺序要一样。

FlowMapper:

1 获取一行数据,转成字符串;

2 切割数据;

3 抓取我们需要的数据:手机号,上行流量,下行流量;

4 封装 outK outV;

5 写出 outK outV;

FlowReducer:

1 遍历 values,将其中的上行流量,下行流量分别累加

2 封装 outKV

3 写出 outK outV

FlowDriver

1 获取 job 对象

2 关联本 Driver 类

3 关联 Mapper 和 Reducer

4 设置 Map 端输出 KV 类型

5 设置程序最终输出的 KV 类型

6 设置程序的输入输出路径

7 提交 Job

注:在设置路径导包要导长的,即import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;,而非import org.apache.hadoop.mapred.FileInputFormat;

MapReduce框架原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPwgEAzs-1635324563426)(MapReduce.assets/image-20210708215548343.png)]

map阶段称为MapTask;Reduce阶段称为ReduceTask

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Amaf7tNF-1635324563428)(MapReduce.assets/image-20210708220449139.png)]

第一点表示切了多少片就开多少MapTask

注:临时缓存staging等目录跟着hadoopproject走,项目创建在f盘那么tmp就在f盘,如下图
在这里插入图片描述

Job提交流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-frvJFb8m-1635324563433)(MapReduce.assets/image-20210709095459314.png)]

切片流程

(1)程序先找到你数据存储的目录。
(2)开始遍历处理(规划切片)目录下的每一个文件
(3)遍历第一个文件ss.txt
a)获取文件大小fs.sizeOf(ss.txt)
b)计算切片大小computeSplitSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M;注:上式可通过调整maxSize以及minSize来调整切片的大小。
c)默认情况下,切片大小=blocksize
d)开始切,形成第1个切片:ss.txt—0:128M 第2个切片ss.txt—128:256M 第3个切片ss.txt—256M:300M每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片
e)将切片信息写到一个切片规划文件中
f)整个切片的核心过程在getSplit()方法中完成
g)InputSplit只记录了切片的元数据信息,比如起始位置、长度以及所在的节点列表等。
(4)提交切片规划文件到YARN上,YARN上的MrAppMaster就可以根据切片规划文件计算开启MapTask个数。

注:job提交过程提供上图中xml、jar包、切片信息

注:每一个文件单独切片。

注:如果总长度小于块大小的1.1倍,则不切分,如:块大小是128m,文件长度为150m,则切两片,分为两个maptask;若文件为129m,则不切片,只用一个maptask。


处理小文件–CombineTextInputFormat

对于小文件较多时的切片,使用CombineTextInputFormat进行切片,将多个小文件合并成一个切片,通过CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);// 4m可以修改一个切片的大小,这里是4m,其具体机制如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qDCTjnmq-1635324563435)(MapReduce.assets/image-20210709154454889.png)]

MapReduce 工作流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IqoGZEe8-1635324563436)(MapReduce.assets/image-20210709164607549.png)]

注:环形缓冲区到80%反向写是在正向一个线程写数据的同时在反向末尾再开一个线程反向写数据,此时两个线程同时写数据不至于像单线程写满半边之后再等单线程向磁盘溢写,溢写结束后才能写新数据这样的工作流程效率较低。

注:上图右下角倒数第二行中~~<a,2>应为<a,1>,<a,1>~~这里是经过combiner就会变成<a,2>,提前在reduce阶段进行合并,减少 reduce阶段的压力,若没有开启combiner,则为<a,1>,<a,1>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oh1XhAIe-1635324563437)(MapReduce.assets/image-20210709165045498.png)]

shuffle机制

Map 方法之后,Reduce 方法之前的数据处理过程称之为 Shuffle。

在这里插入图片描述

注:传输的kv中k一定要是可排序,否则mapreduce会报错,MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。

Partition 分区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gtIGQAYM-1635324563439)(MapReduce.assets/image-20210709210027562.png)]

注:此处提到重写Partition类,之后的driver类中会有将此类嵌入到整个执行流程中去的方法,即job.setPartitionerClass(ProvincePartitioner.class);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryvaALsx-1635324563449)(MapReduce.assets/image-20210709222302451.png)]

reduceTask可以控制实际分区个数,reducetask设置是几输出就是几个文件。

读源码对抽象类和接口的思考

注:在源码中Partitioner类是抽象类,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6hYSLoE8-1635324563450)(MapReduce.assets/image-20210709213953672.png)]

我们先继承此抽象:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-upjOnNrJ-1635324563451)(MapReduce.assets/image-20210709214456942.png)]

在driver类中传递继承类的信息,将我们写的类嵌入整体执行流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x53x3Gd8-1635324563451)(MapReduce.assets/image-20210709214611677.png)]

在源码中MapTask.java类中通过下图方式用抽象类 先初始化partitioner的类型;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oOFyZdzp-1635324563452)(MapReduce.assets/image-20210709213921354.png)]

在经过job.setPartitionerClass(ProvincePartitioner.class);之后,在jobContxt中便存有了partitioner的反射类信息,再通过newinstance传递此信息从而对patitioner进行实例化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KODUXWlI-1635324563452)(MapReduce.assets/image-20210709213934648.png)]

注:在此源码中接口的使用很广泛,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CQ5wMFV2-1635324563453)(MapReduce.assets/image-20210709220605496.png)]

在这里jobContext点进去只是一个接口类型,只有在执行的时候才能看到真正的类的源码,是因为jobContext的类在执行的过程中实现了接口,这里红框中的形参表示一个插座,而实现的接口就像是插头,刚好插在这一块的插头里,个人认为接口的使用是为了整个代码逻辑结构的规范,将每一部分分开开发而又不会乱。而之前抽象类的使用则是放宽了编写程序时对某一对象的类型限制。暂时心得是这里的接口只是为了规范传递参数而提前设置好的插座,而抽象类的使用使为了减少开发的类型限制。

接口与抽象类的区别

相同点

(1)都不能被实例化 (2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点

(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。

(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。

(3)接口强调特定功能的实现,而抽象类强调所属关系。

(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

WritableComparable排序(二次排序)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FfZzIf9x-1635324563454)(MapReduce.assets/image-20210710151518793.png)]

注:此案例的输入数据是FlowBean序列化案例的输出文件。

注:此案例中,需求为根据手机总流量排序而非手机号排序,所以在Mapper中输出的kv是将流量bean作为k,手机号作为v,而在flowbean.java中重写了compareTo方法,在Mapper输出数据时对总流量进行排序,此排序如下图所示部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bzd1S2DM-1635324563455)(MapReduce.assets/image-20210711151759283.png)]

实现代码如下:

@Override
    public int compareTo(FlowBean o) {
        if(this.sumFlow>o.sumFlow){
            return -1;
        }else if(this.sumFlow<o.sumFlow){
            return 1;
        }else{
            return 0;
        }

Combiner合并

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7cBsxJUB-1635324563455)(MapReduce.assets/image-20210711154945846.png)]

在combine不影响业务逻辑的前提下,如求和而不是求平均数时,可直接使用job.setCombinerClass(WordCountReducer.class)来在maptask阶段进行聚合,如下图:

在这里插入图片描述

MapTask工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8uMawGFJ-1635324563456)(MapReduce.assets/image-20210711205840533.png)]

五大阶段如下所示:

(1)Read 阶段:MapTask 通过 InputFormat 获得的 RecordReader,从输入 InputSplit 中解析出一个个 key/value。
(2)Map 阶段:该节点主要是将解析出的 key/value 交给用户编写 map()函数处理,并产生一系列新的 key/value。
(3)Collect 收集阶段:在用户编写 map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的 key/value 分区(调用Partitioner),并写入一个环形内存缓冲区中。
(4)Spill 阶段:即“溢写”,当环形缓冲区满后,MapReduce 会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。

(5)Merge 阶段:当所有数据处理完成后,MapTask 对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。

ReduceTask工作机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQoNTzhF-1635324563457)(MapReduce.assets/image-20210711210921609.png)]

三大阶段如下:

(1)Copy 阶段:ReduceTask 从各个 MapTask 上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
(2)Sort 阶段:在远程拷贝数据的同时,ReduceTask 启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。按照 MapReduce 语义,用
户编写 reduce()函数输入数据是按 key 进行聚集的一组数据。为了将 key 相同的数据聚在一起,Hadoop 采用了基于排序的策略。由于各个 MapTask 已经实现对自己的处理结果进行了局部排序,因此,ReduceTask 只需对所有数据进行一次归并排序即可。
(3)Reduce 阶段:reduce()函数将计算结果写到 HDFS 上。

注意事项:

(1)ReduceTask=0,表示没有Reduce阶段,输出文件个数和Map个数一致。
(2)ReduceTask默认值就是1,所以输出文件个数为一个。
(3)如果数据分布不均匀,就有可能在Reduce阶段产生数据倾斜
(4)ReduceTask数量并不是任意设置,还要考虑业务逻辑需求,有些情况下,需要计算全局汇总结果,就只能有1个ReduceTask。
(5)具体多少个ReduceTask,需要根据集群性能而定。
(6)如果分区数不是1,但是ReduceTask为1,是否执行分区过程。答案是:不执行分区过程。因为在MapTask的源码中,执行分区的前提是先判断ReduceNum个数是否大于1。不大于1肯定不执行。

Reduce Join

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M18ejZnG-1635324563458)(MapReduce.assets/image-20210712160051245.png)]

思路:创建tablebean实体类,在mapper中将pid设置为k,mapper之后的shuffle阶段会自动对pid进行排序,将pid相同的数据传输给reduce进行操作,在reduce中遍历values输出即可。

注:value中的foreach需要创建一个临时对象来存储遍历的数据,再用BeanUtils.copyProperties将遍历的数据拷贝到创建的对象中去,因为orderbean.add添加的是遍历创建对象value的地址,当遍历完之后orderbean中会存的每一个都是最后一次遍历的值。解决代码如下:

for (TableBean value : values){
            if("order".equals(value.getFlag())){
                TableBean tmptableBean = new TableBean();
                try {
                    BeanUtils.copyProperties(tmptableBean, value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                orderBeans.add(tmptableBean);
            }else{
                try {
                    BeanUtils.copyProperties(pdBean,value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

注意代码中创建的临时变量tmptableBean,通过这个变量为中介进行存储。

Map Join

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyIJChvZ-1635324563459)(MapReduce.assets/image-20210712162515702.png)]


总结

1 ) 输入数据接口:InputFormat
(1)默认使用的实现类是:TextInputFormat
(2)TextInputFormat 的功能逻辑是:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为 value 返回。
(3)CombineTextInputFormat 可以把多个小文件合并成一个切片处理,提高处理效率。
2 ) 逻辑处理接口:Mapper
用户根据业务需求实现其中三个方法:map() setup() cleanup ()
3 )Partitioner 分区
(1)有默认实现 HashPartitioner,逻辑是根据 key 的哈希值和 numReduces 来返回一个分区号;key.hashCode()&Integer.MAXVALUE % numReduces
(2)如果业务上有特别的需求,可以自定义分区。
4 )Comparable

(1)当我们用自定义的对象作为 key 来输出时,就必须要实现 WritableComparable 接口,重写其中的 compareTo()方法。
(2)部分排序:对最终输出的每一个文件进行内部排序。
(3)全排序:对所有数据进行排序,通常只有一个 Reduce。
(4)二次排序:排序的条件有两个。

5 )Combiner 合并
Combiner 合并可以提高程序执行效率,减少 IO 传输。但是使用时必须不能影响原有的业务处理结果。
6 ) 逻辑处理接口:Reducer
用户根据业务需求实现其中三个方法:reduce() setup() cleanup ()
7 ) 输出数据接口:OutputFormat
(1)默认实现类是 TextOutputFormat,功能逻辑是:将每一个 KV 对,向目标文本文件输出一行。
(2)用户还可以自定义 OutputFormat。

Hadoop 数据压缩

1 )压缩的好处和坏处
压缩的优点:以减少磁盘 IO、减少磁盘存储空间。
压缩的缺点:增加 CPU 开销。
2 ) 压缩原则
(1)运算密集型的 Job,少用压缩
(2)IO 密集型的 Job,多用压缩

在这里插入图片描述

注:压缩方式选择时重点考虑:压缩/解压缩速度、压缩率(压缩后存储大小)、压缩后是否
可以支持切片。

压缩位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNTV1DHU-1635324563461)(MapReduce.assets/image-20210712202902144.png)]

压缩参数配置

在这里插入图片描述

Map输出端采用压缩

在driver中添加如下代码:

// 开启 map 端输出压缩
conf.setBoolean("mapreduce.map.output.compress", true);
// 设置 map 端输出压缩方式
conf.setClass("mapreduce.map.output.compress.codec",
BZip2Codec.class,CompressionCodec.class);

注:压缩方式可采用BZip2Codec、 DefaultCodec等

Reduce输出端采用压缩

在driver中添加如下代码:

// 设置 reduce 端输出压缩开启
FileOutputFormat.setCompressOutput(job, true);
// 设置压缩的方式
FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);
//以下是不同的压缩方式
// FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
// FileOutputFormat.setOutputCompressorClass(job,DefaultCodec.class);

Yarn:从入门到精通

Yarn 是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台,而 MapReduce 等运算程序则相当于运行于操作系统之上的应用程序。

YARN基础架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7LGTB2k-1635325317313)(Yarn:从入门到精通.assets/image-20210712210656198.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3sfGs77-1635325317317)(Yarn:从入门到精通.assets/image-20210712211715072.png)]

(1)MR 程序提交到客户端所在的节点。
(2)YarnRunner 向 ResourceManager 申请一个 Application。
(3)RM 将该应用程序的资源路径返回给 YarnRunner。
(4)该程序将运行所需资源提交到 HDFS 上。
(5)程序资源提交完毕后,申请运行 mrAppMaster。
(6)RM 将用户的请求初始化成一个 Task。
(7)其中一个 NodeManager 领取到 Task 任务
(8)该 NodeManager 创建容器 Container,并产生 MRAppmaster。

(9)Container 从 HDFS 上拷贝资源到本地。
(10)MRAppmaster 向 RM 申请运行 MapTask 资源。
(11)RM 将运行 MapTask 任务分配给另外两个 NodeManager,另两个 NodeManager
别领取任务并创建容器。
(12)MR 向两个接收到任务的 NodeManager 发送程序启动脚本,这两个 NodeManager
分别启动 MapTask,MapTask 对数据分区排序。
(13)MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。
(14)ReduceTask 向 MapTask 获取相应分区的数据。
(15)程序运行完毕后,MR 会向 RM 申请注销自己。

Yarn调度器和调度算法

目前,Hadoop 作业调度器主要有三种:FIFO、容量(Capacity Scheduler)和公平(FairScheduler)。Apache Hadoop3.1.3 默认的资源调度器是 Capacity Scheduler。

先进先出调度器(FIFO)

FIFO 调度器(First In First Out):单队列,根据提交作业的先后顺序,先来先服务。

优点:简单易懂;
缺点:不支持多队列,生产环境很少使用;

容量调度器(Capacity Scheduler ):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UM6DOjHA-1635325317337)(Yarn:从入门到精通.assets/image-20210713104358741.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2QqOPqj-1635325317340)(Yarn:从入门到精通.assets/image-20210713114116391.png)]

公平调度器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmrU0gbl-1635325317341)(Yarn:从入门到精通.assets/image-20210713144426271.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwneqcFL-1635325317342)(Yarn:从入门到精通.assets/image-20210713144655830.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EnnOpKWR-1635325317344)(Yarn:从入门到精通.assets/image-20210713145105530.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZb5NpIN-1635325317344)(Yarn:从入门到精通.assets/image-20210713155719049.png)]

DRF 策略

DRF(Dominant Resource Fairness),我们之前说的资源,都是单一标准,例如只考虑内存(也是Yarn默认的情况)。但是很多时候我们资源有很多种,例如内存,CPU,网络带宽等,这样我们很难衡量两个应用应该分配的资源比例。

Yarn 生产环境核心参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fb9Ck6Bd-1635325317346)(Yarn:从入门到精通.assets/image-20210713164004968.png)]

配置需求:从 1G 数据中,统计每个单词出现次数。服务器 3 台,每台配置 4G 内存,4 核CPU,4 线程。

1G / 128m = 8 个 MapTask;1 个 ReduceTask;1 个 mrAppMaster

即1G数据,一块大小为128m,所以需要8块,即8个maptask,1个reducetask,1个job

平均每个节点运行 10 个 / 3 台 ≈ 3 个任务(4 3 3)(其中一台服务器运行4个)

末尾添加代码, yarn-site.xml 添加配置参数如下:

<!-- 选择调度器,默认容量 -->
<property>
<description>The class to use as the resource scheduler.</description>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>
<!-- ResourceManager 处理调度器请求的线程数量,默认 50;如果提交的任务数大于 50,可以增加该值,但是不能超过 3 台 * 4 线程 = 12 线程(去除其他应用程序实际不能超过 8) -->
<property>
<description>Number  of  threads  to  handle  schedulerinterface.</description>
<name>yarn.resourcemanager.scheduler.client.thread-count</name>
<value>8</value>
</property>
<!-- 是否让 yarn 自动检测硬件进行配置,默认是 false,如果该节点有很多其他应用程序,建议
手动配置。如果该节点没有其他应用程序,可以采用自动 -->
<property>
<description>Enable auto-detection of node capabilities such as
memory and CPU.
</description>
<name>yarn.nodemanager.resource.detect-hardware-capabilities</name>
<value>false</value>
</property>
<!-- 是否将虚拟核数当作 CPU 核数,默认是 false,采用物理 CPU 核数 -->
<property>
<description>Flag to determine if logical processors(such as hyperthreads) should be counted as cores. Only applicable on Linux when yarn.nodemanager.resource.cpu-vcores is set to -1 and yarn.nodemanager.resource.detect-hardware-capabilities is true.
</description>
<name>yarn.nodemanager.resource.count-logical-processors-as-cores</name>
<value>false</value>
</property>
<!-- 虚拟核数和物理核数乘数,默认是 1.0 -->
<property>
<description>Multiplier to determine how to convert phyiscal cores to vcores. This value is used if yarn.nodemanager.resource.cpu-vcores is set to -1(which implies auto-calculate vcores) and yarn.nodemanager.resource.detect-hardware-capabilities is set to true.The number of vcores will be calculated as number of CPUs * multiplier.
</description>
<name>yarn.nodemanager.resource.pcores-vcores-multiplier</name>
<value>1.0</value>
</property>
<!-- NodeManager 使用内存数,默认 8G,修改为 4G 内存 -->
<property>
<description>Amount of physical memory, in MB, that can be allocated for containers. If set to -1 and yarn.nodemanager.resource.detect-hardware-capabilities is true, it is automatically calculated(in case of Windows and Linux). In other cases, the default is 8192MB.
</description>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>4096</value>
</property>
<!-- nodemanager 的 CPU 核数,不按照硬件环境自动设定时默认是 8 个,修改为 4 个 -->
<property>
<description>Number of vcores that can be allocated for containers. This is used by the RM scheduler when allocating resources for containers. This is not used to limit the number of CPUs used by YARN containers. If it is set to -1 and yarn.nodemanager.resource.detect-hardware-capabilities is true, it is automatically determined from the hardware in case of Windows and Linux. In other cases, number of vcores is 8 by default.</description>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>4</value>
</property>
<!-- 容器最小内存,默认 1G -->
<property>
<description>The minimum allocation for every container request at the RM in MBs. Memory requests lower than this will be set to the value of
this  property. Additionally, a node manager that is configured to have
less memory than this value will be shut down by the resource manager.
</description>
<name>yarn.scheduler.minimum-allocation-mb</name>
<value>1024</value>
</property>
<!-- 容器最大内存,默认 8G,修改为 2G -->
<property>
<description>The maximum allocation for every container request at the
RM in  MBs.  Memory  requests  higher  than  this  will  throw  an InvalidResourceRequestException.
</description>
<name>yarn.scheduler.maximum-allocation-mb</name>
<value>2048</value>
</property>
<!-- 容器最小 CPU 核数,默认 1 个 -->
<property>
<description>The minimum allocation for every container request at the RM in terms of virtual CPU cores. Requests lower than this will be set to the value of this property. Additionally, a node manager that is configured to have fewer virtual cores than this value will be shut down by the resource  manager.
</description>
<name>yarn.scheduler.minimum-allocation-vcores</name>
<value>1</value>
</property>
<!-- 容器最大 CPU 核数,默认 4 个,修改为 2 个 -->
<property>
<description>The maximum allocation for every container request at the RM in terms of virtual CPU cores. Requests higher than this will throw an InvalidResourceRequestException.</description>
<name>yarn.scheduler.maximum-allocation-vcores</name>
<value>2</value>
</property>
<!-- 虚拟内存检查,默认打开,修改为关闭 -->
<property>
<description>Whether virtual memory limits will be enforced for containers.</description>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
<!-- 虚拟内存和物理内存设置比例,默认 2.1 -->
<property>
<description>Ratio between virtual memory to physical memory when setting memory limits for containers. Container allocations are expressed in terms of physical memory, and virtual memory usage  is allowed to exceed this allocation by this ratio.
</description>
<name>yarn.nodemanager.vmem-pmem-ratio</name>
<value>2.1</value>
</property>

配置完xml,分发文件,使用[atguigu@hadoop102 hadoop-3.1.3]$ sbin/stop-yarn.sh
[atguigu@hadoop103 hadoop-3.1.3]$ sbin/start-yarn.sh重启集群。

注意:如果集群的硬件资源不一致,要每个 NodeManager 单独配置

容量调度器多队列

1)在生产环境怎么创建队列?
(1)调度器默认就 1 个 default 队列,不能满足生产要求。
(2)按照框架:hive /spark/ flink 每个框架的任务放入指定的队列(企业用的不是特别
多)
(3)按照业务模块:登录注册、购物车、下单、业务部门 1、业务部门 2

2)创建多队列的好处?
(1)因为担心员工不小心,写递归死循环代码,把所有资源全部耗尽。
(2)实现任务的 降级使用,特殊时期保证重要的任务队列资源充足。11.11 6.18
业务部门 1(重要)=》业务部门 2(比较重要)=》下单(一般)=》购物车(一般)=》
登录注册(次要)

案例

需求 1:default 队列占总内存的 40%,最大资源容量占总资源 60%,hive 队列占总内存
的 60%,最大资源容量占总资源 80%。
需求 2:配置队列优先级

在 capacity-scheduler.xml 中配置如下:

(1)修改如下配置

<!-- 指定多队列,增加 hive 队列 -->
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>default,hive</value>
<description>
The queues at the this level (root is the root queue).
</description>
</property>
<!-- 降低 default 队列资源额定容量为 40%,默认 100% -->
<property>
<name>yarn.scheduler.capacity.root.default.capacity</name>
<value>40</value>
</property>
<!-- 降低 default 队列资源最大容量为 60%,默认 100% -->
<property>
<name>yarn.scheduler.capacity.root.default.maximum-capacity</name>
<value>60</value>
</property>

(2)为新加队列添加必要属性:

<!-- 指定 hive 队列的资源额定容量 -->
<property>
<name>yarn.scheduler.capacity.root.hive.capacity</name>
<value>60</value>
</property>
<!-- 用户最多可以使用队列多少资源,1 表示 -->
<property>
<name>yarn.scheduler.capacity.root.hive.user-limit-factor</name>
<value>1</value>
</property>
<!-- 指定 hive 队列的资源最大容量 -->
<property>
<name>yarn.scheduler.capacity.root.hive.maximum-capacity</name>
<value>80</value>
</property>
<!-- 启动 hive 队列 -->
<property>
<name>yarn.scheduler.capacity.root.hive.state</name>
<value>RUNNING</value>
</property>
<!-- 哪些用户有权向队列提交作业 -->
<property>
<name>yarn.scheduler.capacity.root.hive.acl_submit_applications</name>
<value>*</value>
</property>
<!-- 哪些用户有权操作队列,管理员权限(查看/杀死) -->
<property>
<name>yarn.scheduler.capacity.root.hive.acl_administer_queue</name>
<value>*</value>
</property>
<!-- 哪些用户有权配置提交任务优先级 -->
<property>
<name>yarn.scheduler.capacity.root.hive.acl_application_max_priority</name>
<value>*</value>
</property>
<!-- 任务的超时时间设置:yarn application -appId appId -updateLifetime Timeout
参 考 资 料 : https://blog.cloudera.com/enforcing-application-lifetime-slas-yarn/ -->
<!-- 如果 application 指定了超时时间,则提交到该队列的 application 能够指定的最大超时时间不能超过该值。
-->
<property>
<name>yarn.scheduler.capacity.root.hive.maximum-application-
lifetime</name>
<value>-1</value>
</property>
<!-- 如果 application 没指定超时时间,则用 default-application-lifetime 作为默认值 -->
<property>
<name>yarn.scheduler.capacity.root.hive.default-application-lifetime</name>
<value>-1</value>
</property>

重启 Yarn 或者执行 yarn rmadmin -refreshQueues 刷新队列,就可以看到两条队列。

向Hive队列提交任务方式:

  1. 使用[atguigu@hadoop102 hadoop-3.1.3]$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount -D mapreduce.job.queuename=hive /input /output

  2. 在driver中声明:

    public class WcDrvier {
    public  static  void  main(String[]  args)  throws  IOException,
    	ClassNotFoundException, InterruptedException {
    	Configuration conf = new Configuration();
    	conf.set("mapreduce.job.queuename","hive");
    	//1. 获取一个 Job 实例
    	Job job = Job.getInstance(conf);
    	。。。 。。。
    	//6. 提交 Job
    	boolean b = job.waitForCompletion(true);
    	System.exit(b ? 0 : 1);
    }
    }
    

任务优先级

容量调度器,支持任务优先级的配置,在资源紧张时,优先级高的任务将优先获取资源。默认情况,Yarn 将所有任务的优先级限制为 0,若想使用任务的优先级功能,须开放该限制。

1)修改 yarn-site.xml 文件,增加以下参数

<property>
<name>yarn.cluster.max-application-priority</name>
<value>5</value>
</property>

2)分发配置,并重启 Yarn

3)[atguigu@hadoop102 hadoop-3.1.3]$ hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar pi -D mapreduce.job.priority=5 5 2000000

加粗部分为设置优先级代码

也可以通过以下命令修改正在执行的任务的优先级。

[atguigu@hadoop102 hadoop-3.1.3]$ yarn application -appID application_1611133087930_0009 -updatePriority 5

公平调度器案例

需求:创建两个队列,分别是 test 和 atguigu(以用户所属组命名)。期望实现以下效果:若用户提交任务时指定队列,则任务提交到指定队列运行;若未指定队列,test 用户提交的任务到 root.group.test 队列运行,atguigu 提交的任务到 root.group.atguigu 队列运行(注:group 为用户所属组)。

公平调度器的配置涉及到两个文件,一个是 yarn-site.xml,另一个是公平调度器队列分配文件 fair-scheduler.xml(文件名可自定义)。

(1)配置文件参考资料:
https://hadoop.apache.org/docs/r3.1.3/hadoop-yarn/hadoop-yarn-site/FairScheduler.html
(2)任务队列放置规则参考资料:
https://blog.cloudera.com/untangling-apache-hadoop-yarn-part-4-fair-scheduler-queue-basics/

1.修改 yarn-site.xml 文件,加入以下参数

<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
<description>配置使用公平调度器</description>
</property>
<property>
<name>yarn.scheduler.fair.allocation.file</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/fair-scheduler.xml</value>
<description>指明公平调度器队列分配配置文件</description>
</property>
<property>
<name>yarn.scheduler.fair.preemption</name>
<value>false</value>
<description>禁止队列间资源抢占</description>
</property>

2.配置 fair-scheduler.xml

<?xml version="1.0"?>
<allocations>
<!-- 单个队列中 Application Master 占用资源的最大比例,取值 0-1 ,企业一般配置 0.1
-->
<queueMaxAMShareDefault>0.5</queueMaxAMShareDefault>
<!-- 单个队列最大资源的默认值 test atguigu default -->
<queueMaxResourcesDefault>4096mb,4vcores</queueMaxResourcesDefault>
<!-- 增加一个队列 test -->
<queue name="test">
<!-- 队列最小资源 -->
<minResources>2048mb,2vcores</minResources>
<!-- 队列最大资源 -->
<maxResources>4096mb,4vcores</maxResources>
<!-- 队列中最多同时运行的应用数,默认 50,根据线程数配置 -->
<maxRunningApps>4</maxRunningApps>
<!-- 队列中 Application Master 占用资源的最大比例 -->
<maxAMShare>0.5</maxAMShare>
<!-- 该队列资源权重,默认值为 1.0 -->
<weight>1.0</weight>
<!-- 队列内部的资源分配策略 -->
<schedulingPolicy>fair</schedulingPolicy>
</queue>
<!-- 增加一个队列 atguigu -->
<queue name="atguigu" type="parent">
<!-- 队列最小资源 -->
<minResources>2048mb,2vcores</minResources>
<!-- 队列最大资源 -->
<maxResources>4096mb,4vcores</maxResources>
<!-- 队列中最多同时运行的应用数,默认 50,根据线程数配置 -->
<maxRunningApps>4</maxRunningApps>
<!-- 队列中 Application Master 占用资源的最大比例 -->
<maxAMShare>0.5</maxAMShare>
<!-- 该队列资源权重,默认值为 1.0 -->
<weight>1.0</weight>
<!-- 队列内部的资源分配策略 -->
<schedulingPolicy>fair</schedulingPolicy>
</queue>
<!-- 任务队列分配策略,可配置多层规则,从第一个规则开始匹配,直到匹配成功 -->
<queuePlacementPolicy>
<!-- 提交任务时指定队列,如未指定提交队列,则继续匹配下一个规则; false 表示:如果指定队列不存在,不允许自动创建-->
<rule name="specified" create="false"/>
<!-- 提交到 root.group.username 队列,若 root.group 不存在,不允许自动创建;若root.group.user 不存在,允许自动创建 -->
<rule name="nestedUserQueue" create="true">
<rule name="primaryGroup" create="false"/>
</rule>
<!-- 最后一个规则必须为 reject 或者 default。Reject 表示拒绝创建提交失败,default 表示把任务提交到 default 队列 -->
<rule name="reject" />
</queuePlacementPolicy>
</allocations>

3.分发配置并重启 Yarn

提交任务时不指定队列,按照配置规则,任务会到 root.atguigu.atguigu 队列

hadoop jar /opt/module/hadoop-3.1.3/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar pi 1 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVjkCjr7-1635325317346)(Yarn:从入门到精通.assets/image-20210714102730681.png)]

Yarn 的 的 Tool 接口案例

说明:使用hadoop jar wc.jar com.atguigu.mapreduce.wordcount2.WordCountDriver -Dmapreduce.job.queuename=root.test /input /output1代码执行程序会报如下错误:

[jiangdaxia@hadoop102 hadoop-3.1.3]$ hadoop jar wc.jar com.jiangdaxia.MapReduce.wordcount2.WordCountDriver -Dmapreduce.job.queuename=root.test /input /output1
2021-07-14 10:39:33,965 INFO client.RMProxy: Connecting to ResourceManager at hadoop103/192.168.10.103:8032
0 [main] INFO org.apache.hadoop.yarn.client.RMProxy  - Connecting to ResourceManager at hadoop103/192.168.10.103:8032
Exception in thread "main" org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory hdfs://hadoop102:8020/input already exists

原因是将-Dmapreduce.job.queuename=root.test此参数误认为是第一个参数,即输入路径,而第二个参数\input被当成了输出路径而导致的错误,所以报\input已存在。

解决方案:

创建类 WordCount 并实现 Tool 接口:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import
org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import java.io.IOException;
public class WordCount implements Tool {
private Configuration conf;
@Override
public int run(String[] args) throws Exception {
Job job = Job.getInstance(conf);
job.setJarByClass(WordCountDriver.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
return job.waitForCompletion(true) ? 0 : 1;
}
@Override
public void setConf(Configuration conf) {
this.conf = conf;
}
@Override
public Configuration getConf() {
return conf;
}
public static class WordCountMapper extends
Mapper<LongWritable, Text, Text, IntWritable> {
private Text outK = new Text();
private IntWritable outV = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value,
Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] words = line.split(" ");
for (String word : words) {
outK.set(word);
context.write(outK, outV);
}
}
}
public static 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;
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.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.util.Arrays;
public class WordCountDriver {
private static Tool tool;
public static void main(String[] args) throws Exception {
// 1. 创建配置文件
Configuration conf = new Configuration();
// 2. 判断是否有 tool 接口
switch (args[0]){
case "wordcount":
tool = new WordCount();
break;
default:
throw new RuntimeException(" No such tool: "+
args[0] );
}
// 3. 用 Tool 执行程序
// Arrays.copyOfRange 将老数组的元素放到新数组里面
int run = ToolRunner.run(conf, tool,Arrays.copyOfRange(args, 1, args.length));
System.exit(run);
}
}

以上代码注意ToolRunner.run(conf, tool,Arrays.copyOfRange(args, 1, args.length));,这里只选取最后两个参数作为输入输出目录。这样便可使用yarn jar YarnDemo.jar com.atguigu.yarn.WordCountDriver wordcount -Dmapreduce.job.queuename=root.test /input /output1来执行。

注:在maven项目中更换jdk版本需要:

1.设置maven的setting.xml

2.将repository目录更换(因为原来下的依赖不能用了,要新建目录)

3.右键项目在open modules setting里面修改language level

4.在SDKs里面修改jdkpath

5.在setting里面修改target bytecode version

总结及面试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OaaTqK6u-1635325317348)(Yarn:从入门到精通.assets/image-20210714113055112.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值