1、Yarn资源调度与隔离
a、三种资源调度Resource Scheduler(ResourceManager处理)
1、FIFO Scheduler:按照作业的提交顺序放到先进先出的队列中执行;
2、Capacity Scheduler(雅虎):将不同作业放到不同队列中,每个队列按照FIFO或者DRF进行分配资源;Apache版本默认使用的,性能比FIFO好......
3、Fair Scheduler(Facebook):动态划分或指定多个队列,每个队列按照Fair(默认)或者FIFO或者DRF(主资源公平算法)进行分配资源;cdh版本默认使用的
注意:DRF算法(主资源公平算法)
作业1:cpu资源是主资源
作业2:内存资源是主资源
b、三种资源调度Resource Scheduler介绍配置
1、Capacity Scheduler介绍和配置
Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能力。通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源,这样整个集群就可以通过设置多个队列的方式给多个组织提供服务了。除此之外,队列内部又可以垂直划分,这样一个组织内部的多个成员就可以共享这个队列资源了,在一个队列内部,资源的调度是采用的是先进先出(FIFO)策略。
在正常的操作中,Capacity调度器不会强制释放Container,当一个队列资源不够用时,这个队列只能获得其它队列释放后的Container资源。当然,我们可以为队列设置一个最大资源使用量,以免这个队列过多的占用空闲资源,导致其它队列无法使用这些空闲资源,这就是”弹性队列”需要权衡的地方。
1.1、配置(capacity-scheduler.xml)
<configuration>
<!-- 定义了两个子队列prod和dev -->
<property>
<name>yarn.scheduler.capacity.root.queues</name>
<value>prod, dev</value>
</property>
<!-- dev队列又被分成了eng和science -->
<property>
<name>yarn.scheduler.capacity.root.dev.queues</name>
<value>eng, science</value>
</property>
<!-- 队列prod占40%的容量 -->
<property>
<name>yarn.scheduler.capacity.root.prod.capacity</name>
<value>40</value>
</property>
<!-- 队列dev占60%的容量 -->
<property>
<name>yarn.scheduler.capacity.root.dev.capacity</name>
<value>60</value>
</property>
<!-- 限制dev的最大资源伸缩比重为75%,所以即使prod队列完全空闲dev也不会占用全部集群资源 -->
<property>
<name>yarn.scheduler.capacity.root.dev.maximum-capacity</name>
<value>75</value>
</property>
<!-- 队列eng占50%的容量,由于没有设置最大值,所以可能占用整个父队列的资源 -->
<property>
<name>yarn.scheduler.capacity.root.dev.eng.capacity</name>
<value>50</value>
</property>
<!-- 队列science占50%的容量,由于没有设置最大值,所以可能占用整个父队列的资源 -->
<property>
<name>yarn.scheduler.capacity.root.dev.science.capacity</name>
<value>50</value>
</property>
</configuration>
Capacity容器除了可以配置队列及其容量外,我们还可以配置一个用户或应用可以分配的最大资源数量、可以同时运行多少应用、队列的ACL认证等。
在MapReduce中,我们可以通过mapreduce.job.queuename属性指定要用的队列。如果队列不存在,我们在提交任务时就会收到错误。如果我们没有定义任何队列,所有的应用将会放在一个default队列中。
注意:对于Capacity调度器,我们的队列名必须是队列树中的最后一部分,如果我们使用队列树则不会被识别。即不能写成dev.eng,应该写为eng。
1.2、配置yarn-site.xml
<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>mapreduce.job.queuename=eng)
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount -Dmapreduce.job.queuename=eng file:/usr/local/big_data/hadoop/hadoop-3.1.1/NOTICE.txt file:/opt/output
查看调度器:http://10.18.6.107:8088/cluster中的Scheduler
2、Fair Scheduler介绍和配置
Fair调度器的设计目标是为所有的应用分配公平的资源(对公平的定义可以通过参数来设置)。举个例子,假设有两个用户A和B,他们分别拥有一个队列。当A启动一个job而B没有任务时,A会获得全部集群资源;当B启动一个job后,A的job会继续运行,不过一会儿之后两个任务会各自获得一半的集群资源。如果此时B再启动第二个job并且其它job还在运行,则它将会和B的第一个job共享B这个队列的资源,也就是B的两个job会用于四分之一的集群资源,而A的job仍然用于集群一半的资源,结果就是资源最终在两个用户之间平等的共享。
直接运行作业,就会创建一个当前用户登录用户名为队列名的队列运行,如果指定了队列名就会在指定的队列运行
执行:bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount file:/usr/local/big_data/hadoop/hadoop-3.1.1/NOTICE.txt file:/opt/output1
2.2、有fair-scheduler.xml(先去掉yarn-site.xml中yarn.resourcemanager.scheduler.class,capacity-scheduler.xml还原)
<?xml version="1.0"?>
<allocations>
<!-- 默认调度策略,如果没有配置这项,默认fair -->
<defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy>
<queue name="prod">
<!-- 权重,如果没有配置默认为1 -->
<weight>40</weight>
<schedulingPolicy>fifo</schedulingPolicy>
</queue>
<queue name="dev">
<weight>60</weight>
<queue name="eng"/>
<queue name="science"/>
</queue>
<!--
queuePlacementPolicy元素定义规则列表,会逐个尝试直到匹配成功。
第一个规则specified,则会把应用放到它指定的队列中,若这个应用没有指定队列或队列名不存在,则不匹配这个规则;
primaryGroup规则会尝试把应用以用户所在的Unix组名命名的队列中,如果没有这个队列,不创建队列转而尝试下一个;
当前面所有规则不满足时,则触发default规则,把应用放在dev.eng队列中
-->
<queuePlacementPolicy>
<rule name="specified" create="false"/>
<rule name="primaryGroup" create="false"/>
<rule name="default" create="dev.eng"/>
</queuePlacementPolicy>
</allocations>bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount file:/usr/local/big_data/hadoop/hadoop-3.1.1/NOTICE.txt file:/opt/output2
c、资源隔离(NodeManager管理)
nodemanager为运行的不同的container进程提供可独立使用的计算资源,以避免它们之间相互干扰。目前支持两种类型的资源隔离:Cpu和内存,对于这两种类型的资源,yarn使用了不同的资源隔离方案。
yarn container支持两种实现:DefaultContainerExecutor和LinuxContainerExecutor;其中DefaultContainerExecutor不支持cpu和资源隔离,LinuxContainerExecutor使用Cgroup的方式支持cpu的资源隔离,两者内存的资源隔离都是通过“线程监控”的方式实现的。
注意:内存使用量超过预先定义的上限值的情况,使用Cgroup进行内存资源隔离会强制杀死进程。
1、含义:nodemanager运行多个controller进程,进程需要的资源需要进行隔离,不让彼此之间产生干扰。
2、隔离方式:cpu隔离、内存隔离
3、yarn container两种执行方式:
DefaultContainerExecutor(内存隔离)和LinuxContainerExecutor(内存隔离、cpu隔离(Cgroup))
注意:两种方式的内存隔离都是采用线程监控方式
内存监控(线程监控)
monitoringThread线程每隔一段时间扫描正在运行的container进程;
应用程序配置参数:
mapreduce.map.memory.mb:MapReduce Map task需要使用的内存量(单位:MB);
NodeManager配置参数:
yarn.nodemanager.pmem-check-enable:NodeManager是否启用物理内存量监控,默认值:true
yarn.nodemanager.vmem-check-enable:NodeManager是否启用虚拟内存量监控,默认值:true
yarn.nodemanager.vmem-pmem-ratio:NodeManager虚拟内存与物理内存的比例,默认值:2:1
yarn.nodemanager.resource.memory-mb:NodeManager最多可以使用多少物理内存,默认值:8G
cpu隔离(Cgroup需要安装,安装路径 https://www.jianshu.com/p/e283ab7e2530)
cd /usr/local/big_data/hadoop/hadoop-3.1.1/etc/hadoop
d、yarn四大组件(ResourceManager(RM)、NodeManager(NM)、ApplicationMaster(AM)、Container)
yarn架构图:
e、yarn流程图
f、组件作用
1、ResourceManager(处理客户端请求、启动/监控ApplicationMaster、监控NodeManager、资源分配与调度)
2、ApplicationMaster(程序切分、为应用程序申请资源,并分配任务、任务监控与容错)
3、NodeManager(单节点上的资源管理、处理来自ResourceManager的命令、处理来自ApplicationMaster的命令)
4、Container(对任务运行环境的抽象,封装了cpu、内存等多维资源以及环境变量、启动命令等任务运行相关的信息)
2、ntp时间服务器
1、作用:同步三个服务器的时间,让服务器一致
注意:机器1当成时间服务器,机器2/3同步机器1时间。
2、在机器1上
2.1、vim /etc/sysconfig/ntpd
SYNC_HWCLOCK=yes
vim /etc/ntp.conf
restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap # 在192.168.1.0网段
# server 0.centos.pool.ntp.org iburst
# server 1.centos.pool.ntp.org iburst
# server 2.centos.pool.ntp.org iburst server 127.127.1.0
fudge 127.127.1.0 stratum 1
0
保存
2.3、重启ntp服务器
service ntpd restart
2.4、设置时间(集群1):
date -s 2018-06-25
date -s 10:30:30
2.5、机器2/3同步时间
2.5.1、手动同步:
/usr/sbin/ntpdate <主机名机器1>
2.5.2、定时同步:
crontabl -e
0-59/10 * * * * /usr/sbin/ntpdate <主机名机器1(时间服务器)> 每十分钟来一次
注意:crontab格式:分 小时 天 月 周 <command>
3、MapReduce原理与实战
执行流程:
wordcount执行流程:
a、InputFormat(抽象类)功能与实现类
1、功能:a、对输入文件进行切分,生成InputSplit切片;b、创建RecordReader,将InputSplit交给Mapper进程读取。
2、InputFormat实现类:DBInputFormat、FileInputFormat、EmptyInputFormat。
FileInputFormat(处理文件的InputFormat):
1、TextInputFormat:文件按行划分,key是这一行在文件中的偏移量,value是这一行文本;
2、KeyValueTextInputFormat:读取普通文本文件,文件按照行分割,每一行由key和value组成,key和value的分隔符如果没有指定,那么整行为key,valu为空;
3、SequenceFileInputFormat:从sequenceFile读取,<key,value>键值对存放的文件;
4、NLineInputFormat:是可以将N行数据划分为一个split,作为MapTask输入;
5、CombineFileInputFormat:合并多个小文件成为一个分片;
DBInputFormat:主要用于处理数据库数据的InputFormat类;
3、InputFormat功能分析
b、SequenceFileInputFormat使用
1、生成sequenceFile文件,<key,value>形式二进制文件
public static void main(String[] args) throws IOException {
//1、SequenceFile文件是通过SequenceFile类生成的
Configuration configuration = new Configuration();
//hadoop配置项、name文件名、keyClass:key的数据类型、valueClass:值的数据类型
//指定文件名称
Writer.Option name = Writer.file(new Path("file:/f:/test/wordcount_1"));
//指定key的类型
Writer.Option keyClass = Writer.keyClass(LongWritable.class);
//指定value的类型
Writer.Option valueClass = Writer.valueClass(Text.class);
//创建输出流
Writer writer = SequenceFile.createWriter(configuration, name, keyClass, valueClass);
//读取文本文件
FileSystem fileSystem = FileSystem.get(configuration);
FSDataInputStream in = fileSystem.open(new Path("file:/f:/test/wordcount.txt"));
String line = null;
Long num = 0L;
while((line = in.readLine()) != null){
//不断递增key值
num ++;
//输出每行数据到SequenceFile中
writer.append(new LongWritable(num), new Text(line));
}
IOUtils.closeStream(writer);
} 2、SequenceFileInputFormat使用
public class WordCountWithSequenceFile {
static{
System.setProperty("hadoop.home.dir", "F:\\big_data\\hadoop\\hadoop-3.1.1");
}
/**
* WordCountMapper
* 默认MapReduce是通过TextInputFormat进行切片,并交给Mapper进行处理
* TextInputFormat: key:当前行的首字母的索引,value:当前行数据
* Mapper类参数:输入key类型:Long,输入Value的类型:String,输出key类型:String;输出value类型:Long
* @version 2019年2月25日上午8:32:36
* @author wuliu
*/
public static class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable>{ LongWritable one = new LongWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String words = value.toString();
String[] wordAdd = words.split(" ");
for (String word : wordAdd) {
//输出格式<单词,1>
context.write(new Text(word), one);
}
}
}
/**
* 进行全局聚合
* Reducer参数:输入key:String,输入value类型:long;输出key类型:String,输出value类型Long
* @version 2019年2月25日上午8:39:08
* @author wuliu
*/
public static class WordCountReduce extends Reducer<Text, LongWritable, Text, LongWritable>{ /**
* 将map输出结果进行全局聚合
* key:单词,values:个数[1,1,1]
*/
protected void reduce(Text key, Iterable<LongWritable> values, Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
Long sum = 0L;
for (LongWritable value : values) {
//累加单词个数
sum += value.get();
}
//输出最终数据结果
context.write(key, new LongWritable(sum));
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration configuration = new Configuration();
//连接Hadoop环境(远程调用运行)
// 1、创建一个job
// Job job = Job.getInstance();
Job job = Job.getInstance(configuration, "word-count");
//通过类名打成jar包
job.setJarByClass(WordCountWithSequenceFile.class);
// 2、输入文件
FileInputFormat.addInputPath(job, new Path(args[0]));
//指定SequenceFileInputFormat处理SequenceFile文件
job.setInputFormatClass(SequenceFileInputFormat.class);
// 3、编写mapper处理逻辑
job.setMapperClass(WordCountMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
// 4、shuffle流程(暂时不用处理)
// 5、编写Reduce处理逻辑
job.setReducerClass(WordCountReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
// 6、输出文件
FileOutputFormat.setOutputPath(job, new Path(args[1])); // //序列化文件的压缩类型:None、Block、Record
// SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK);
// //压缩格式:default、gzip、lz4、snappy
// SequenceFileOutputFormat.setOutputCompressorClass(job, DefaultCodec.class);
// job.setOutputFormatClass(SequenceFileOutputFormat.class);
//7、运行job
boolean result = job.waitForCompletion(true);
System.out.println(result ? 1 : 0);
}
} file:/e:/sf
file:/e:/out
c、InputSplit原理
1、什么时候切分
client端进行切分,切分后交给YARN服务器执行
client端提交job时,根据输入文件的大小进去切片,每个输入切片对应一个map任务,切片存储的并非数据本身,而是一个切片长度和一个记录数据位置的数组
2、切片中存储的内容
数据长度、数据存储位置
3、切片大小
minSize=max{minSplitSize,mapred.min.split.size}
maxSize =mapred.max.split.size
splitSize = max{minSize,min{maxSize,blockSize}}
minSplitSize默认大小为1B
4、切片数量(即mapper进程数量)
mapper数: 总文件大小/切片大小
d、Reduce个数
设置:通过job.setNumReduceTask(n)设定或配置mapreduce.job.reduces(默认是1)
建议:合适Reduce task数量是0.95或者1.75*(nodes * mapreduce.tasktracker.reduce.tasks.maxmum),其中,mapreduce.tasktracker.reduce.tasks.maxmum的数量一般设置为各节点cpu core数量,即能同时计算的slot数量。对于0.95,当map结束时,所有的Reduce能够立即启动;对于1.75,较快的节点结束第一轮Reduce后,可以开始第二轮的Reduce任务,从而提高负载均衡。
e、OutputFormat
1、功能:校验job中指定输出路径是否存在,将结果写入输出文件(将结果输出到表或文件汇总)
2、实现类:DBOutputFormat、FileOutputFormat、NullOutputFormat。
FileOutputFormat(将Reduce结果写入文件中):
1、MapFileOutputFormat:主要是处理MapFile(特殊的SequenceFile)的输出;
2、TextFileOutputFormat:主要是处理普通文本的输出,也是默认实现;
3、SequenceFileOutputFormat:主要是处理SequenceFile的输出;
4、FilterOutputFormat:主要就是方便包装其他OutputFormat;
5、MultipleOutputs:将结果输出到多个文件中;
DBOutputFormat:发生Reduce结果到sql表中
f、partition分区器
1、功能:
1.1、位置:在Mapper和Reducer处理逻辑之间,shuffle写入流程开始的时候;
1.2、将map输出结果分发到不同的Reduce上,负载均衡尽量将数据均匀的分配到不同的Reduce;
1.3、分区数和Reducer数量一样;
2、Partitioner子类:
2.1、HashPartitioner
2.2、KeyFieldBasedPartitioner也是基于hash的partitioner
2.3、BinaryPartitioner
2.4、TotalOrderPartitioner这个类可以实现输出的全排序,这个类不是基于hash的,对排序数据进行抽样,抽样数据进行排序生成标尺,将数据发到对应区间id的Reduce。
h、shuffle流程
1、位置
在Mapper和Reducer处理逻辑之间,连接map和Reduce的纽带
2、功能
shuffle的本义是洗牌、混洗,把一组有一定规则的数据尽量转换成一组无规则的数据,越随机越好。MapReduce中的Shuffle更像是洗牌的逆过程,把一组无规则的数据尽量转换成一组具有一定规则的数据。
从Map输出到Reduce输入的整个过程可以广义地称为shuffle。shuffle横跨map端和Reduce端,在map端包括spill写过程,在Reduce端包括copy和sort读过程。
shuffle写入流程(map端)
shuffle读取流程(Reduce端)
3、流程图(1、2、3不同颜色代码不同分区,取是通过copy的)
4、shuffle写入流程
a、map任务输出数据经过分区,分区完后通过collect收集到内存环形缓冲区kvbuffer
b、sort将缓冲区中的数据排序(1、按分区排序;2、每个分区中数据按key进行排序)
c、spill线程溢写到本地磁盘(每次缓冲区满了就溢写,会产生很多小文件)
d、merge合并将小文件合并到大文件
split过程包括输出、排序、溢写、合并等步骤
5、shuffle读取流程-copy
Reduce任务通过Http向各个Map任务拖取它所需要的数据。
如果内存可以放下就直接放到内存中,每个map数据对应一块空间。当内存空间达到一定程度就启动内存merge,将数据输出到一个磁盘文件中。
如果内存放不下就把map数据直接写到磁盘上。一个map数据就建一个文件,当文件数达到一定阀值,就启动磁盘merge,合并到一个文件。
最终对内存和磁盘上的数据进行全局合并。
shuffle读取流程-merge sort
这里使用的merge和map端使用的merge过程一样。map的输出数据已经是有序的,merge进行一次合并并排序,所谓Reduce端的sort过程就是这个合并的过程。一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。
4、聚合日志
a、含义:分布式计算作业放到NodeManager上运行,日志信息放在NodeManager本地目录:
yarn.nodemanager.log-dirs:${yarn.log.dir}/userlogs
b、通过配置将本地日志放到HDFS服务器上,即聚合日志的概念
c、配置yarn-site.xml
yarn.log-aggregation-enable 是否启用日志聚合功能,日志聚合开启后保存到HDFS上;
yarn.log-aggregation.retain-seconds 聚合后的日志在HDFS上保存多长时间,单位为s;
yarn.log-aggregation.retain-check-interval-seconds 删除任务在HDFS上执行的间隔,执行时候将满足条件的日志删除(超过保存时间的日志),如果是0或者负数,则为参数2设置值的1/10;
yarn.nodemanager.log.retain-seconds当不启用日志聚合此参数生效,日志文件保存在本地的时间,单位s;
yarn.nodemanager.remote-app-log-dir 当应用程序运行结束后,日志被转移到HDFS目录(启用日志聚集功能时有效),修改为保存的日志文件夹
yarn.nodemanager.remote-app-log-dir-suffix 远程日志目录子目录名称(启用日志聚集功能时有效)
测试:
<!-- 启用日志聚合功能-->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property> <!-- 存放多长时间-->
<property>
<name> yarn.log-aggregation.retain-seconds</name>
<value>3600</value>
</property> d、历史服务器
1、配置项
mapred-default.xml
mapreduce.jobhistory.address jobhistory的rpc访问地址
mapreduce.jobhistory.webapp.address jobhistory的http访问地址
2、启动/访问/停止
sbin/mr-jobhistory-daemon.sh start historyserver
yarn主界面跳转:WEB UI : http://ip:19888
停止: sbin/mr-jobhistory-daemon.sh stophistoryserver
bin/hdfs dfs -ls /tmp