Hadoop2.X基于yarn资源管理的MapReduce

MapReduce

Map端:中间级映射。

Reduce端:相同的key为一组,组的迭代计算。一组key迭代一次。

MapTask 和 ReduceTask 计算向数据移动

块:

一个文件切成若干个快(实实在在被切割的块),

散列的存储在各个节点上

然后计算(map)向数据(block)移动

split:片

(逻辑上被切割的片,默认一个片大小就是一个块大小=128M)

map和split是一一对应的

也就是说,数量上,块<=片=Map

为什么要有片?

假如一个文件对应的·1有十个块,如果没有片,块太大了,一个块128M对应一个Map,10个Map,每个Map同时计算每个的128M需要10分钟。

如果有了片,Map端就灵活了,假如每个物理块切成十个逻辑片,每个片对应一个Map,一共有100个Map,每个Map计算12.8M的逻辑片,需要的时间就是1分钟,效率就大大提高。

MapReduce流程

Map 先做完k,v集映射,生成了大量的k,v,然后交给reduce迭代计算。相同的k做一组迭代计算。(如果最后有几组k,就准备几个reduce迭代计算,是可以的,但是不够优化,有负载均衡问题),一个reduce可以处理多组相同的k的。但是一组k必须在同一个reduce里迭代计算。(这也是mr框架最死板的地方。到了strom可以进行不同的分发的)。这是mr的分发策略。

例如:统计各个城市的房价

北京统计200万条

上海统计100万条

深圳统计50万条

青岛统计10万条

map端:k,v中间级映射。k->城市 v->房价。 map的数量取决于split逻辑片>=物理块。最后共生成360万条k,v。

reduce端:相同的k只能在同一个reduce迭代计算,一个reduce可以迭代计算多组k。所以reduce<=k的种类数目,reduce端的效率取决于最高组k的或者说任务量最大的那个reduce。

MapReduce分为四个阶段:

1:split逻辑切片

2 : 拿到逻辑上切片之后,生成map开始计算

3:shuffle

4:reduce

shuffle和缓冲区

如果没有shuffle,大量的k,v 直接就拉取到reduce进行迭代计算,这样的话,整个mr框架的效率就会非常低。会有磁盘上大量的交互。就好比:外面点了12瓶啤酒,一瓶一瓶的送·过来,虽然每次送的速度非常快,但是效率还是很低。

map task流程

input split ->map->buffer in memory ->partition,sort and spill to disk partitions -> merge on disk

缓冲区

split切片后,生成map,每个map之后会开辟一块内存缓冲区

分区的时候根据reduce task数量决定分区的数量(不设定是默认一个reduce task),怎么分到每个分区里是根据哈希分的,相同的k一组。

一个内存缓冲区会有多个partition分区,partition分区,每个partition分区对应一个reduce

有几个partion分区就有几个reduce。每个reduce都知道自己拉取对应的哪个partiton分区。

一次排序:(按照不同partition分区来排到对应的partition分区)

在缓冲区里边会进行排序,每个分区都有排好序的北京上海青岛深圳,reduce端正好也有对应的去接收。

reduce1只拉取所有排好序的内存缓冲区的第一部分的k->北京,reduce2只拉取第二部分的k->上海…

reduce有多少个由自己设定,有多少个reduce就决定有多少个分区编号。(分区不是内存缓冲区,而是在内存缓冲区里进行的分区,分区编号从0开始,0,1,2,3.)

二次排序:(按照partition分区内部来排)

假如自己定义两个reduce,reduce1->北京上海,reduce2->青岛深圳。缓冲区里有分区,一次排序后,每个缓冲区里生成了两个分区,分区1是北京上海,但是北京上海是乱序的,分区是2青岛深圳,他们内部也是乱序的。如果就这样把乱序的北京上海直接交给reduce1,那reduce得到的就是乱序的。相同的k调用一组reduce方法,如果直接这样,会有大量的IO查找相同的k,遍历很多次。

所以就在分区内部进行第二次排序,同一个分区里的北京上海进行二次排序,青岛深圳之间也进行相互排序。

除了排序,还要进行combiner压缩,如果在reduce端还是要sum value ,直接在内存缓冲区完成计算压缩。最后每个缓冲区的分区里的一组k只有一个k,v传递过去,减少与IO磁盘数据量的交互。

三次排序;每个map在生成k.v的时候还会生成一个缓冲区,缓冲区里有partition分区,每次缓冲区快满的时候会发生溢写操作,生成文件在磁盘上,时间长了,会有好多个溢写的小文件,这些小文件之间会进行上诉的combiner,生成大文件生成大文件之间会进行一次排序,叫做归并。

四次排序:每个map最后生成的多个小文件文件会进行多次归并一个大文件,多个map之间每个map都有一个归并的大文件,这些大文件之间还要进行多次排序归并,到最后归并成只有几个的大文件。reduce端才会拉取,不一定非要只归并成一个大文件才拉取,因为每次的归并也是IO流的交互。

reduce task

相同的k调用一组reduce方法。

总流程各个分工

map:

​ 1 读懂数据

2 映射为kv模型 自己根据需求情况设定

​ 3 并行分布式 多个map 在多个并行服务器上运行,多个reduce在多个服务器上运行

​ 4 计算向数据移动 把map task 和reduce task 向block块上移动

reduce:

​ 1 数据全量/分量加工(partiton/group)

​ partiton>group

​ partiton:每个reduce的k可以是多个,但是只有一个partition

​ group:每组k对应一个group,也就是说一个partition包含一个到多个group

​ 2 reduce中可以包含不同的key

​ 3 相同的key汇聚到一个reduce中(死板的分发策略)

​ 4 相同的key调用一次reduce方法

​ 排序实现key的汇聚

k,v使用自定义数据类型(所有基本数据类型都不支持,必须是特殊的封装类,都是对象类型)

1 作为参数传递,节省开发成本,提高程序自由度

2 Writable 序列化:能使分布式程序相互据交互(对象类型需要序列化)

3 Comparable比较器:实现具体排序(字典序,数值序等)

mr版本

hadoop 1.x -> mr 1.x

Clients: 任务(job)是clients完成规划的,这个任务需要切多少片,需要多少map,map能跑到哪些节点之上,从哪里读取这些数据呢,又把这些数据输出到哪里呢,Clients完成它的作业是通过API环境,通过Java这种环境来写的。

客户端设计作业:需要多少rerduce->多少分区->块的大小可以通过client完成->知道块的数量->知道切片数量->知道map数量

设计好了打好jar包给hdfs,给job有缺陷,job是单点,没备份。

Job Tracter :

​ 任务调度:指定相关的节点去开辟map task reduce task,需要跟Namenode沟通,才能知道节点具体位置在哪里,才能指定Task Tracter 开辟相关任务

​ 资源管理:整个资源的利用情况由job Tracker维护,每个Task Tracker 掌握当前节点的资源利用情况,所以Job Tracker 与Task Tracker 时时刻刻保持通讯情况

Task Tracter:任务跟踪器

​ 掌握自己节点的资源利用信息

​ 开启map task reduce task

​ 还得知道自己要干什么,所以要从namenode拿jar包提取任务下载到自己的datanode节点之上,开启计算任务

总结:

mr 1 角色:

​ job tracker

​ 核心,主,单点

​ 调度所有作业(按照client提交的作业,按照吩咐做,哪些开辟map,哪些reduce)

​ 监控整个集群的资源负载(job 和 task 共同用完成的,通过task汇报,自己能掌控下层的资源使用情况)

​ 对jar包的获知

​ task tracker

​ 从,自身节点资源管理

​ 和job tracker心跳,汇报资源,获取task(按照job 的吩咐在我的节点上开启相应的任务,还要主动的申请)

​ client

​ 作业为单位,打jar包(Job)

​ 规划作业的计算分布(用到哪些节点了,切多少片啊)

​ 提交作业资源到HDFS(打jar包提交)

​ 最终提交作业到JOBTracker

弊端:

​ job tracter:负载过重,单点故障

​ 资源管理与计算调度强耦合,其他计算框架需要重复实现资源管理

​ hadoop计算框架job tracker 在node1 node3 开辟了相应的map task和redece task,这时又来了个计算框架 strom 主节点nimbus 在 从节点node1上也想开辟map任务,但是nimbus和job tracker 他们之间是不通信的,所以nimbus不知道这个节点的资源已经被占用了。

​ 不同框架对资源不能全局管理

mr 2 版本

在mr1 升级到2 后,mr拆分成 mr2和yarn

基于yarn的资源管理和分配(放权,不让jobTracker 这一组件承担过多的功能)

yarn:资源管理框架

zookeeper:大数据分布式协调框架

​ 原job tracker:

​ Resource Manager :资源管理

​ Application master:任务调度和任务监控

​ 一个job作业对应一个app mast(如果app mast挂了,resources manager 会赶紧创建一个新的)

​ 原 TaskTracker:

​ NodeManager:负责执行原task tracker任务

Container:容器,默认1G

长服务:从集群搭建开始,一直到关闭,这个服务(进程)一直存在

1.x:namenode, datanode ,secondary namenode

2.x:node namager ,resource manager

短服务:app mstr(提交任务过来,resource manager才会创建app mstr)

mr2总结:

​ yarn:解耦资源与计算

​ ResourceManager

​ 主,核心

​ 集群节点资源管理

​ NodeManager(通过管理Container的生命周期,专门管理自己本节点的资源情况)

​ 与RM汇报资源

​ 管理Container生命周期

​ 计算框架中角色都以Container表示

​ Container:[节点NM,CPU.MEM.I/O大小,启动命令]

​ 默认NodeManager启动线程监控Container大小,超出申请资源额度,kill

​ 支持Linux内核的Cgroup

​ mr:

​ mr-ApplicationMaster-Container(任务调度作业角色,在哪个节点之上开辟container,container上开启map task reduce task等)

​ 作业为单位,避免单点故障,负载到不同的节点

​ 创建Task需要resourcemanager申请资源(Container / MR 1024MB)

​ task container

​ Client:

​ RM-Client:请求资源创建AM

​ AM-Client:与AM交互

两个版本总结:

yarn:yet another resource negotiator

hadoop2.0

​ 核心思想:将MRv1中JobTracker的资源管理和任务调度两个功能分开,分别由ResourceManager和ApplicationMaster进程实现

​ ResourceManager:负责整个集群的资源管理和调度

​ ApplicationMaster:负责应用程序相关的事务,比如任务调度,任务监控和容错等。

yarn的引入,使得多个计算框架可运行在一个集群中

​ 每个应用程序对应一个ApplicationMaster

​ 目前多个计算框架可以运行在YARN上,比如Map Reduce,Spark,Storm等

高可用搭建环境配置

node nn-1 nn-2 DN ZK ZKFC JNN RS NM

6 * * *

7 * * * * * *

8 * * * * * *

9 * * *

RS 是长服务 (分散) 放在 8 9

ResourceManager High Avaliability

NM也是长服务(分散) 放在 7 8 9

从官网查看 yarn HA 和 single

重命名mapred-site.xml

修改/hadoop-2.6.5/etc/hadoop/mapred-site.xml

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

修改/hadoop-2.6.5/etc/hadoop/yarn-site.xml

//低可用
<configuration>
    <property>
        <name>yarn.nodemanager.aux=services</name>
        <value>mapreduce_shuffle</value>
    </property>
    
//HA
<property>
  <name>yarn.resourcemanager.ha.enabled</name>
  <value>true</value>
</property>
<property>
  <name>yarn.resourcemanager.cluster-id</name>
  <value>cluster1</value>
</property>
<property>
  <name>yarn.resourcemanager.ha.rm-ids</name>
  <value>rm1,rm2</value>
</property>
<property>
  <name>yarn.resourcemanager.hostname.rm1</name> master1 改成 node8
  <value>master1</value>
</property>
<property>
  <name>yarn.resourcemanager.hostname.rm2</name> master2 改成 node9
  <value>master2</value>
</property>

<property>
  <name>hadoop.zk.address</name>
  <value>zk1:2181,zk2:2181,zk3:2181</value>//zk1改成 node7 zk2改成node2 zk3 改成node3
</property>
</configuration>

修改完成之后配置文件分发到其他节点 在hadoop目录上执行命令

# scp mapred-site.ml yarn-site.xml node7:`pwd`

# scp mapred-site.ml yarn-site.xml node8:`pwd`

# scp mapred-site.ml yarn-site.xml node9:`pwd`

node8 node9 自身免密钥 相互免密钥

自己免密钥

1 生成密钥

# ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa

2 公钥追加到钥匙集合

# cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

ssh 对方免密钥

1 生成密钥

# ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa

2 把公钥分发到对方节点

# scp ~/.ssh/id_rsa.pub node9:`pwd`/node8.pub

3 在对方节点上输入命令

# cat node8.pub >>authorized_keys

启动集群

1 先启动 zk

all# zkServer.sh start

2 再启动hdfs

# start-dfs.sh

3 启动 yarn

# start-yarn.sh	

启动hdfs和yarn可以一起启动

# start-all.sh

4 最后在RM的两个主节点启动RM(node7,node8)

# yarn-daemon.sh start resourcemanager

5 查看yarn

node8:8088

利用自带的mapreduce的wordcount jar包计算

进入软件包里的分享目录里的hadoop里的mr,里面有自带的jar包

/opt/sxt/hadoop-2.6.5/share/hadoop/mapreduce

执行计算命令

# hadoop jar hadoop-mapreduce-examples-2.6.5.jar wordcount /user/root/text.txt /wordcount

执行完成hdfs的/wordcount目录里会出现 两个文件

success(只是告诉你这个任务计算成功了)

part-r-00000(这里面是计算的结果)

part 代表 partition分区 000000代表分区号,因为没有规定分区,所以默认一个分区,分区号00000

查看part-r-00000

1.用eclipse或者idea利用hafs文件系统对象配合IO流查看

2.利用命令查看

# hadoop fs -cat /wordcount/part-r-00000

# hdfs dfs -cat /wordcount/part-r-00000

关闭集群

1 先在node7和node8上关闭 resourcemanager

# yarn-daemon.sh stop resourcemanager

2 然后在主节点上关闭 hdfs和yarn

# stop.all.sh

3.然后关闭zookeeper

all# zkServer.sh stop

idea写MapReduce代码

1 MRDemo类

package mapreduce;

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;

/**
 * 创建配置文件对象
 *      new Configuration
 * 创建一个Job对象,封城jar包提交
 *      Job.getInstance(conf)
 */
public class MRDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        // 1.创建配置文件对象
        Configuration conf = new Configuration();

        //2 创建Job对象
        Job job = Job.getInstance(conf);
        //3.指定打Jar包的类
        job.setJarByClass(MRDemo.class);

        //4.设置作业名称,也可以不设置
        job.setJobName("myjob");

        //5.指定输入获取路径
        Path inPath = new Path("/user/root/text.txt");
        //6.通过这个格式化这个类添加要获取的文件和写出的文件
        //这个类作用很多,继承InputFormat,先于Job执行,切片,添加路径都是这个类
        FileInputFormat.addInputPath(job,inPath);

        Path outPath = new Path("/output/wordcount/");
        //如果输出路径存在,则删除
        if (outPath.getFileSystem(conf).exists(outPath)){
            outPath.getFileSystem(conf).delete(outPath,true);
        }
        FileOutputFormat.setOutputPath(job,outPath);

        //设置map和reduce 服务器节点之间数据传输需要序列化
        //但是这个内部已经实现序列化,但是还要告知内部我这个已经序列化的类型
        job.setMapperClass(MyMapper.class);
        //告知类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        job.setReducerClass(MyReducer.class);

        //提交当前的job作业
        job.waitForCompletion(true);
    }
}

  1. MyMapper类
package mapreduce;

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

import java.io.IOException;
import java.util.StringTokenizer;


/**
 * Mapper:
 *      1.四个自定义泛型类型<KEYIN,VALUEIN,KEYOUT,VSLUEOUT>
 *          KEYIN:输入给map之前的数据 (一行数据的Key,当前行的首字母下标索引,因为还没有split切割)
 *                  数字类型:不能写基本类型 Object(LongWritable)
 *          VALUEIN:一行数据的Value
 *                  文本类型:Text(不能写String)
 *
 *          KEYOUT:
 *                  文本类型:Text(切割后)
 *          VALUEOUT:
 *              map的数据  INTWritable
 *
 *      1.map方法执行一次就只有一行数据,run方法的作用就是循环多次执行map方法
 *      为什么重写map方法,而不重写run方法
 *      因为run方法,本身就是实现了有数据就循环执行map方法,不需要重写
 *      而map方法本身只是原样输出,想要实现自己的需求,自己必须重写
 *
 */
public class MyMapper extends Mapper<Object, Text, Text, IntWritable> {

    //one 对应的是VALUEOUT
    private final static IntWritable one = new IntWritable(1);
    //word 对应的是KEYOUT
    private Text word = new Text();

    @Override
    protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
        //迭代器 传进来VALUEIN,对一整行文本进行split切割,扔到迭代器itr中
        StringTokenizer itr = new StringTokenizer(value.toString());//value对应VALUEIN 是一行文本

        //遍历,是否有下一个单词,有的话获取
        while (itr.hasMoreTokens()){
            //把itr的String类型封装到word中,每次循环,都封装一次新的单词
            word.set(itr.nextToken());
            //把单词对应值连接起来,所以最后map的结果都是
            // value全是1(zz,1)(zz,1)(yy,1)(zz,1)(yy,1)(xx,1)
            context.write(word,one);
        }

    }

}

3 MyReduce类

package mapreduce;

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

import java.io.IOException;

/**
 *  Reducer:
 *        四个泛型:
 *              前两个泛型类型对应Mapper的后两个泛型类型 Text,IntWritable
 *              后两个泛型类型和前两个可以一样 Text,IntWritable
 *
 *         1.迭代计算
 *              同一组key做一次迭代计算
 *              map端传过来的数据是(因为map端已经做了排序) (xx,1)(xx,1)(yy,1)(zz,1)(zz,1)(zz,1)
 *              所有key相同做一次迭代计算
 *              xx迭代计算做完,当前迭代计算就结束了一次
 *              然后就是迭代yy
 *
 */
public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    //迭代计算 这个是Reduce结果的value
    private IntWritable result = new IntWritable();

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

        //做累加
        int sum = 0;

        //遍历values 累加
        for (IntWritable val: values
             ) {
            //sum累加 1 1 1
            sum += val.get();
        }
        //封装成IntWritable类型
        result.set(sum);
        //key,value连接
        context.write(key,result);

    }
}

提交代码

idea打jar包

进入project sturcture

选择 Artifacts

点击左上角 +

选择JAR -> From modules with depe…

然后指定好执行类

然后选择Build -> Build Artifacts

选择 …:jar -> Build

然后从out/artifacts目录里找到jar包

Linux执行Jar

hadoop jar Jar包名 执行类名路径

# hadoop jar hadoop_demo.jar mapreduce.MRDemo

然后查看执行结果

1 node8:8088 浏览器查看

2 命令查看

# hadoop fs -cat /output/wordcount/part-r-00000
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值