Haddoop大数据教程笔记_05_Hadoop之MapReduce原理及Yarn相关

目录

Hadoop之MapReduce原理及Yarn相关

MapReduce简介

自动化调度平台——YARN

YARN集群启动

mapreduce Java编程实例:

1.extends Mapper类,并重写map(){}方法,实现map的逻辑:

2.extends Reducer类,并重写reduce(){}方法,实现reduce的逻辑:

3.job的客户端程序,提交mapreduce

4.在Hadoop集群某节点上提交job

5.本地已安装Hadoop windoows版,可以直接在本地提交到本地,方便调试

6.通过重写Reduce类cleanup方法,并加载配置参数,控制输出聚合后的输出条数.

7.通过对象实现 继承实现 WritableComparable (序列化) 实现控制(修改)map 对key的排序逻辑,并实现分区控制.

8.Partitioner分区组件:使用实现了compareTo()的对象作为 map key,在默认key.hashCode()分区时不在同一分区.修改分区逻辑.

9.GroupingComparator分组排序组件:使用实现了compareTo()的对象作为 map key,在聚合时,还要修改分组聚合逻辑,以对象id作为key聚合.

10.mapreduce数据倾斜skew--利用Combiner组件 maptask端局部聚合数据来减轻倾斜影响.

11.控制输入输出格式

12.可以使用内部类一次性实现,Mapper,Reducer, Main(JobSubmitter).复杂逻辑可以通过分步MapReduce实现.

Hadoop-MapReduce核心原理总结:

mapreduce要点复习:


Hadoop之MapReduce原理及Yarn相关

MapReduce简介

MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。主要思想是函数式编程语言中的"Map"和"Reduce"概念,来实现海量数据运算的“分而治之”。

Hadoop-mapreduce是hadoop中一个批量计算的框架,在整个mapreduce作业的过程中,包括从数据的输入,数据的处理,数据的数据输出这些部分,而其中数据的处理部分就要map,reduce,combiner等操作组成。在一个mapreduce的作业中必定会涉及到如下一些组件:

1、客户端,提交mapreduce作业

2、yarn资源管理器,负责集群上计算资源的协调

3、yarn节点管理器,负责启动和监控集群中机器上的计算容器(container)

4、mapreduce的application master,负责协调运行mapreduce的作业

5、hdfs,分布式文件系统,负责与其他实体共享作业文件

自动化调度平台——YARN

hadoop1.x版本JobTracker的作用是资源管理和任务的调度,但不支持多个计算框架。

当存在多个计算框架时,比如说spark,如果两个计算框架都有着自己的资源管理模块,就会存在资源竞争,不便于管理。

此时就需要一个公共的资源管理模块,这就产生了YARN.

hadoop2.x上的mapreduce是基于YARN 的,YARN支持多个计算框架,就比如说刚才说的SPARk.

Yarn上可以支持多个计算框架(MapReduce,spark)

Yarn上的每一个Node Manager 都与每一个dataNode与之对应

Yarn原理过程:

一个客户端A向resource manager 提交请求,resource manager就会会产生一个application master(相当于mapreduce1.x中的jobTracker中的调度模块),resource manager会在hdfs中获取split的信息(被切成了多少块),然后向resource manager申请有多少个mapTask,然后resource manager 会产生多个Container分布在多个节点上(分布在哪个节点上由resource manager控制)

一个客户端B向resource manager 提交请求,resource manager又会会产生一个application master(相当于mapreduce1.x中的jobTracker中的调度模块),resource manager会在hdfs中获取split的信息(被切成了多少块),然后向resource manager申请有多少个mapTask,然后resource manager 会产生多个Container(默认占用空间1个G)分布在多个节点上(分布在哪个节点上由resource manager控制)

Resource manager 又会存在单点故障,此时又需要用到ha来实现高可用性。

YARN集群启动

yarn集群中有两个角色:

主节点:Resource Manager  1台

从节点:Node Manager   N台

Resource Manager一般安装在一台专门的机器上

Node Manager应该与HDFS中的data node重叠在一起

Hadoop安装包自带YARN只需配置yarn-site.xml文件即可。

<property>
    <name>yarn.resourcemanager.hostname</name>
    <value>hdp-03</value>
</property>​    
<property>    
    <name>yarn.nodemanager.aux-services</name>    
    <value>mapreduce_shuffle</value>    
</property>​    
<property>    
    <name>yarn.nodemanager.resource.memory-mb</name>    
    <value>2048</value>    
</property>​    
<property>    
    <name>yarn.nodemanager.resource.cpu-vcores</name>    
    <value>2</value>    
</property>

mapred-site.xml文件中的配置:

<configuration>    <!—mapreduce 基于yarn来实现离线计算的,基础开关 -->    
<property>    
    <name>mapreduce.framework.name</name>    
    <value>yarn</value>    
</property>    
</configuration>

    注意:

    yarn.nodemanager.resource.memory-mb 

    默认配置尾8G,最低建议配置2048,

    在maptask,reducetask启动之前需要启动application master默认需要占用1536(1.5G)内存,

    配置不够时,启动yarn不受影响,但是启动MapReduce时,会报错内存不足

    但是实际上在启动后,并不一定会占用1.5g的内存,往往开启是任务极少内存占用极少,随任务累积变大。

    yarn.nodemanager.resource.cpu-vcores 逻辑CPU,根据运算能力划分,默认8核

    参数配置应参考集群实际硬件配置设置

然后复制到每一台机器上 (以下步骤已实现的可忽略。)

  scp /root/apps/hadoop-2.10.0/etc/hadoop/yarn-site.xml master:$(pwd)

然后在hdp-04上,修改hadoop的slaves文件,列入要启动nodemanager的机器

然后将hdp-04到所有机器的免密登陆配置好

然后,就可以用脚本启动yarn集群:

        sbin/start-yarn.sh      start-yarn.sh

             停止:

    sbin/stop-yarn.sh      stop-yarn.sh

启动完成后,可以在windows上用浏览器访问resourcemanager的web端口:

http://hdp-04:8088

mapreduce Java编程实例:

Hadoop的MapReduce框架已经实现了 map -> shuffle ->reduce 之间的通信过程。

所以我们的代码实现主要有3个:

1.extends Mapper类,并重写map(){}方法,实现map的逻辑:

 /**     
* KEYIN :是map task读取到的数据的key的类型,是一行的起始偏移量Long     
* VALUEIN:是map task读取到的数据的value的类型,是一行的内容String     
* KEYOUT:是用户的自定义map方法要返回的结果kv数据的key的类型,在wordcount逻辑中,我们需要返回的是单词String     
* VALUEOUT:是用户的自定义map方法要返回的结果kv数据的value的类型,在wordcount逻辑中,我们需要返回的是整数Integer     
* 但是,在mapreduce中,map产生的数据需要传输给reduce,需要进行序列化和反序列化,而jdk中的原生序列化机制产生的数据量比较冗余,就会导致数据在mapreduce运行过程中传输效率低下     
* 所以,hadoop专门设计了自己的序列化机制,那么,mapreduce中传输的数据类型就必须实现hadoop自己的序列化接口     
* hadoop为jdk中的常用基本类型Long String Integer Float等数据类型封住了自己的实现了hadoop序列化接口的类型:LongWritable,Text,IntWritable,FloatWritable     
*/    
public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{                

    @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){          
                context.write(new Text(word), new IntWritable(1));                  
        }      
    }    
}


 

2.extends Reducer类,并重写reduce(){}方法,实现reduce的逻辑:

public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

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

		int count = 0;
		Iterator<IntWritable> iterator = values.iterator();
		while (iterator.hasNext()) {
			IntWritable value = iterator.next();
			count += value.get();
		}
		context.write(key, new IntWritable(count));

	}

}

3.job的客户端程序,提交mapreduce

/**
 * 用于提交mapreduce job的客户端程序
 * 功能:
 *   1、封装本次job运行时所需要的必要参数
 *   2、跟yarn进行交互,将mapreduce程序成功的启动、运行
 */
public class JobSubmitter {
	public static void main(String[] args) throws Exception {	
		// 在代码中设置JVM系统参数,用于给job对象来获取访问HDFS的用户身份
		System.setProperty("HADOOP_USER_NAME", "root");	
		
		Configuration conf = new Configuration();
		// 1、设置job运行时要访问的默认文件系统
		conf.set("fs.defaultFS", "hdfs://master:9000");
		// 2、设置job提交到哪去运行
		conf.set("mapreduce.framework.name", "yarn");
		conf.set("yarn.resourcemanager.hostname", "hdp-03");
		// 3、如果要从windows系统上运行这个job提交客户端程序,则需要加这个跨平台提交的参数
		conf.set("mapreduce.app-submission.cross-platform","true");
		
		Job job = Job.getInstance(conf);
		// 1、封装参数:jar包所在的位置
		job.setJar("d:/wc.jar");
		//job.setJarByClass(JobSubmitter.class);
		// 2、封装参数: 本次job所要调用的Mapper实现类、Reducer实现类
		job.setMapperClass(WordcountMapper.class);
		job.setReducerClass(WordcountReducer.class);
		// 3、封装参数:本次job的Mapper实现类、Reducer实现类产生的结果数据的key、value类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		Path output = new Path("/wordcount/output");
		FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"),conf,"root");
		if(fs.exists(output)){
			fs.delete(output, true);
		}
		
		// 4、封装参数:本次job要处理的输入数据集所在路径、最终结果的输出路径
		FileInputFormat.setInputPaths(job, new Path("/wordcount/input"));
		FileOutputFormat.setOutputPath(job, output);  // 注意:输出路径必须不存在
		// 5、封装参数:想要启动的reduce task的数量
		job.setNumReduceTasks(2);
		// 6、提交job给yarn
		boolean res = job.waitForCompletion(true);
		System.exit(res?0:-1);
	}
}	

4.在Hadoop集群某节点上提交job

/**
 * 如果要在hadoop集群的某台机器上启动这个job提交客户端的话
 * conf里面就不需要指定 fs.defaultFS   mapreduce.framework.name
 * 因为在集群机器上用 hadoop jar xx.jar cn.edu360.mr.wc.JobSubmitterLinuxToYarn 命令来启动客户端main方法时,
 *   hadoop jar这个命令会将所在机器上的hadoop安装目录中的jar包和配置文件加入到运行时的classpath中
 *   那么,我们的客户端main方法中的new Configuration()语句就会加载classpath中的配置文件,自然就有了 
 *   fs.defaultFS 和 mapreduce.framework.name 和 yarn.resourcemanager.hostname 这些参数配置
 */
public class JobSubmitterLinuxToYarn {
	
	public static void main(String[] args) throws Exception {
		
		Configuration conf = new Configuration();
		conf.set("fs.defaultFS", "hdfs://master:9000");
		conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
		// 没指定默认文件系统
		// 没指定mapreduce-job提交到哪运行
		Job job = Job.getInstance(conf);
		job.setJarByClass(JobSubmitterLinuxToYarn.class);
		//同上
	}
}

5.本地已安装Hadoop windoows版,可以直接在本地提交到本地,方便调试

Configuration conf = new Configuration();
//因本地配置就是默认配置,因此可以不设置参数。
//conf.set("fs.defaultFS", "file:///");
//conf.set("mapreduce.framework.name", "local");
Job job = Job.getInstance(conf);

//其他加载配置参数方法:
//通过加载classpath下的*-site.xml文件解析参数  
//Configuration()会默认加载 classpath下的*-site.xml文件
Configuration conf = new Configuration();

//通过加载解析自定义xml配置文件,设置参数
//conf.addResource("xx-oo.xml");

// 通过代码设置参数
//conf.setInt("top.n", 3);

//通过main 函数 运行时传参 设置参数
//conf.setInt("top.n", Integer.parseInt(args[0]));

//通过属性配置文件获取参数
/*Properties props = new Properties();
props.load(JobSubmitter.class.getClassLoader().getResourceAsStream("topn.properties"));
conf.setInt("top.n", Integer.parseInt(props.getProperty("top.n")));*/

6.通过重写Reduce类cleanup方法,并加载配置参数,控制输出聚合后的输出条数.

public class PageTopnReducer extends
	Reducer<Text, IntWritable, Text, IntWritable> {

	// 使用  自动排序二叉树结构 Map实现类 存储结构  用来存放Map后的数据结果。
	// HasMap时无序的,Map的另一个实现了类LinkedHashMap 是按照插入顺序有序的,但不会自动排序
	TreeMap<PageCount, Object> treeMap = new TreeMap<>();

	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,
			Reducer<Text, IntWritable, Text, IntWritable>.Context arg2)
			throws IOException, InterruptedException {
		int count = 0;
		for (IntWritable value : values) {
			count += value.get();
		}
		PageCount pageCount = new PageCount();
		pageCount.set(key.toString(), count);
		treeMap.put(pageCount, null);
	}
	
	/**
	 * 通过重写cleanup方法,并加载配置参数,控制输出聚合后的输出条数.
	 */
	@Override
	protected void cleanup(Context context) throws IOException,
			InterruptedException {
		Set<Entry<PageCount,Object>> entrySet = treeMap.entrySet();
		Configuration conf = new Configuration();
		int topn = conf.getInt("top.n",5);
		int i = 0;
		for (Entry<PageCount, Object> entry : entrySet) {
			context.write(new Text(entry.getKey().getPage()), new IntWritable(entry.getKey().getCount()));
			i++;
			if(i==topn){
				return;
			}
		}
	}
}

7.通过对象实现 继承实现 WritableComparable (序列化) 实现控制(修改)map 对key的排序逻辑,并实现分区控制.

public class OrderBean implements WritableComparable<OrderBean>{
	
	private String orderId;
	...
	private float amountFee;
	
	public void set(String orderId, String userId, String pdtName, float price, int num){
		this.orderId = orderId;
		...
		this.amountFee = price * num;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		this.orderId = in.readUTF();
		...
		this.amountFee = this.price * this.num;
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(this.orderId);
		...
		out.writeFloat(this.amountFee);
	}
	
	/**
	 * String 的 compareTo() 方法    
	 * 	this.orderId.compareTo(o.getOrderId())  --> 正序
	 * 	o.getOrderId().compareTo(this.orderId)  --> 逆序
	 */
	@Override
	public int compareTo(OrderBean o) {
		return this.orderId.compareTo(o.getOrderId())==0?Float.compare(o.getAmountFee(), this.amountFee):this.orderId.compareTo(o.getOrderId());
	}

}

8.Partitioner分区组件:使用实现了compareTo()的对象作为 map key,在默认key.hashCode()分区时不在同一分区.修改分区逻辑.

public class OrderIdPartitioner extends Partitioner<OrderBean, NullWritable>{
	@Override
	public int getPartition(OrderBean key, NullWritable v, int numPartitions) {
		// 以orderId 分区 分发数据
		return (key.getOrderId().hashCode() & Integer.MAX_VALUE) % numPartitions;
	}
}

/**
 * Hadoop MapReduce中对于默认分区的源码:
 * 1. 其中key.hashCode(),是对map输出的key取hashCode值
 * 2. & 是java中位运算符,在数据的二进制层面上按位与的意思,两个操作数中位都为1,结果才为1,否则结果为0;
 * 3. 综合而言,key.hashCode() & Integer.MAX_VALUE 
 *    是要保证任何map输出的key在与numReduceTasks取模后决定的分区为正整数。
 */
public int getPartition(K2 key, V2 value, int numReduceTasks) {
	return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}

9.GroupingComparator分组排序组件:使用实现了compareTo()的对象作为 map key,在聚合时,还要修改分组聚合逻辑,以对象id作为key聚合.

public class OrderIdGroupingComparator extends WritableComparator{

	// 构造方法调用父类构造方法指定比较对象,并创建实例。
	public OrderIdGroupingComparator(){
		super(OrderBean.class,true);
	}
	
	//以下两个方法功能一致
	@Override
	public int compare(WritableComparable a, WritableComparable b) {
		OrderBean o1 = (OrderBean) a;
		OrderBean o2 = (OrderBean) b;
		return super.compare(o1.getOrderId(), o2.getOrderId());
	}

	@Override
	public int compare(Object a, Object b) {
		OrderBean o1 = (OrderBean) a;
		OrderBean o2 = (OrderBean) b;
		return super.compare(o1.getOrderId(), o2.getOrderId());
	}		
}

10.mapreduce数据倾斜skew--利用Combiner组件 maptask端局部聚合数据来减轻倾斜影响.

局部聚合实际也是extens Reducer类,如果实际逻辑与 ReduceTask一致,可直接指定ReduceTask的Reducer实现类.

以上3个组件,Partitioner/GroupingComparator/Combiner组件,均需要在Jobsubmitter()中指定.

// 设置分区逻辑类
job.setPartitionerClass(OrderIdPartitioner.class);
// 设置分组排序聚合逻辑类
job.setGroupingComparatorClass(OrderIdGroupingComparator.class);
// 设置maptask端的局部聚合逻辑类
job.setCombinerClass(SkewWordcountReducer.class);

10.1.mapreduce数据倾斜skew的通用解决方案--打散倾斜的key(map阶段给key拼接随机字符串)

第1步map阶段给key拼接随机字符串,打散数据量很大的key:

public static class SkewWordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	Random random = new Random();
	int numReduceTasks = 0;
	Text k = new Text();
	IntWritable v = new IntWritable();
	@Override
	protected void setup(Context context) throws IOException,
			InterruptedException {
		numReduceTasks = context.getNumReduceTasks();
	}
	@Override
	protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException {
		String[] words = value.toString().split("");
		for (String w : words) {
			k.set(w+"\001"+random.nextInt(numReduceTasks));
			v.set(1);
			context.write(k, v);
		}
	}
}

第2步map阶段把key拼接de随机字符串去掉,再聚合:

public static class SkewWordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	
	Text k = new Text();
	IntWritable v = new IntWritable();
	
	@Override
	protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException {
		String[] words = value.toString().split("\t");
		v.set(Integer.parseInt(words[1]));
		String[] split = words[0].split("\001");
		k.set(split[0]);
		context.write(k, v);
	}
}

11.控制输入输出格式

// job.setInputFormatClass(TextInputFormat.class); 默认的输入组件
job.setInputFormatClass(SequenceFileInputFormat.class);
// job.setOutputFormatClass(TextOutputFormat.class);  // 这是默认的输出组件
job.setOutputFormatClass(SequenceFileOutputFormat.class);

12.可以使用内部类一次性实现,Mapper,Reducer, Main(JobSubmitter).复杂逻辑可以通过分步MapReduce实现.

public class FriendsTwo {

	public static class FriendsTwoMapper extends
			Mapper<LongWritable, Text, Text, Text> {

		Text k = new Text();
		Text v = new Text();

		@Override
		protected void map(LongWritable key, Text value, Context context)
				throws IOException, InterruptedException {
			String[] split = value.toString().split("\t");
			k.set(split[0]);
			v.set(split[1]);
			context.write(k, v);
		}
	}

	public static class FriendsTwoReducer extends
			Reducer<Text, Text, Text, Text> {

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

			ArrayList<String> userList = new ArrayList<>();
			for (Text user : users) {
				userList.add(user.toString());
			}
			Collections.sort(userList);
			StringBuffer sb = new StringBuffer();
			for (String user : userList) {
				sb.append(user).append(" ");
			}
			context.write(key, new Text(sb.toString()));
		}
	}

	public static void main(String[] args) throws Exception {

		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);
		job.setJarByClass(FriendsTwo.class);

		job.setMapperClass(FriendsTwoMapper.class);
		job.setReducerClass(FriendsTwoReducer.class);

		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Text.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);
		
		//以上一步输出路径为输入路径
		FileInputFormat.setInputPaths(job, new Path(
				"e:\\wordcount\\output\\friends"));
		FileOutputFormat.setOutputPath(job, new Path(
				"e:\\wordcount\\output\\friends1"));
		job.waitForCompletion(true);
	}
}

Hadoop-MapReduce核心原理总结:

mapreduce程序在YARN上启动-运行-注销的全流程:

mapreduce要点复习:

发布了10 篇原创文章 · 获赞 1 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Snowing_inhope/article/details/105577699
今日推荐