hadoop临时文件 jar包_实战案例玩转Hadoop --Map Reduce上手实践

第6章 Map Reduce上手实践

导读

学习一门框架编程技术,在了解框架整体功能特性和工作机制后,快速上手的方式就是利用这个框架来写出属于自己的第一个程序。
本章就以非常典型且能相当好地诠释MAP REDUCE特性的词频统计为例,以详尽的步骤,引导读者成功开发并运行自己的第一个MAP REDUCE分布式数据处理程序。

6.1 Map Reduce入门编程案例

6.1.1 案例需求

假设有大量文本文件,内容样例如下:

hello jack
hello tom
hello jim
tom and jack are good friends
jim and tom are good friends too

需要统计出所有文件中各单词出现的总次数,即词频统计。

6.1.2 思路设计

回顾

MAP REDUCE程序中,整个数据处理的流程分为map和reduce两个阶段,而这两个阶段中真正工作的程序是Map Task和Reduce Task,这两个程序已在框架中提供,而用户在编写自己的MAP REDUCE程序时,则只需要为Map Task和Reduce Task提供进行数据处理时的具体逻辑函数,即map()函数和reduce()函数。

初学阶段开发mapreduce程序时,可以不用过多考虑mapreduce程序运行时的状况,只要抓住mapreduce编程模型中的要点即可,即考虑map阶段读取到原始数据后产生出什么样的key和value,以便能在reduce阶段按key分组聚合运算时能得到需求的结果。

本例逻辑相对简单,说明如下:

  • map阶段:
  1. Map Task会从HDFS的源数据文件中逐行读取数据,然后对每一行调用一次用户自定义的map()方法;
  2. 用户自定义的map()函数中:
  3. 将每一行数据切分出单词;
  4. 为每一个单词构造一个键值对(单词,1);
  5. 将键值对返回给Map Task。
  • reduce阶段:
  1. Reduce Task会获取map阶段输出的单词键值对;
  2. Reduce Task会将key相同(即单词相同)的键值对汇聚成组;然后对每一组kv数据调用一次用户自定义的reduce()方法来运算处理;
  3. 在用户自定义的reduce()函数中:
  4. 遍历reduce Task所传入的一组kv数据,将value值累加求和,即得到每一个单词的总次数;
  5. 将计算后得到的(单词,总次数)返回给reduce task。

wordcount逻辑示意图:

da26f580e8b3582f245a2a6bdda76d88.png
图6.1 WordCount数据流转流程

6.1.3 关键技术点解析

我们在开发自定义的Mapper类时,需要定义输入数据的泛型类型(KEY IN,VALUE IN)。而这些泛型的确定,依赖于输入数据的类型。

输入数据的类型决定机制描述如下:

Map Task默认情况下是调用TextInputFormat获取一个LineRecordReader来读取文件,其机制为逐行读取,每读到一行数据,就将这一行在文件中的起始偏移量(类型为long)作为key数据,行的文本内容(类型为String)作为value数据;

map()函数处理后产生的数据也需要组织成一个key和一个value,也需要为此声明输出数据的泛型类型(KEY OUT, VALUE OUT);这两个泛型的声明,则取决于我们自定义的map()方法将要生成的结果key-value数据的类型;

而mapreduce中,数据经常需要持久化到磁盘文件,或经网络连接进行传输,而不管持久化到文件还是通过网络传输,key-value数据都需要序列化和反序列化操作。为了降低数据的体积,提高效率,Hadoop开发了一种自己的系列化框架。上述的key-value数据类型也就不能直接采用JDK中提供的原生类型,而是需要使用实现了Hadoop序列化接口的类型,如下:

  1. KEY IN : 行的起始偏移量,原生类型long,对应的Hadoop序列化类型:LongWritable
  2. VALUE IN:行的文本内容,原生类型String,对应的Hadoop序列化类型:Text
  3. KEY OUT: 一个单词,原生类型String,对应的Hadoop序列化类型:Text
  4. VALUE OUT: 单词次数1,原生类型int,对应的Hadoop序列化类型:IntWritable

提示

所谓的序列化,就是将内存中的java对象结构数据“压扁”成一个线性的二进制数据序列,反序列化则反之。由jdk中的序列化机制所生成的二进制序列中,不仅包含对象中的数据,还包含很多附加信息(如类名、继承关系等),换句话说就是jdk的序列化机制所产生的序列化结果非常臃肿和冗余。

在数据量很大且需要频繁进行磁盘序列化和网络传输的情况下,如果用jdk的原生序列化机制,会导致数据传输和存储的负担太大。所以Hadoop开发了一套自己的序列化框架,对象序列化后包含的信息仅限于对象中的数据内容,从而大大降低磁盘存储和网络传输的负担,提高效率。

由于本书侧重于快速上手和实战,限于篇幅所限,对于细节性原理的阐述不便面面俱到,如有需要更详细更透彻地理解,可扫描二维码来获取更多文档资料和视频讲解进行学习。

6.1.4 工程搭建

在eclipse中新建一个工程,并引入用于开发MapReduce程序的jar包;

方式1:使用maven的方式引入jar包

使用maven来引入依赖,可以在工程的pom.xml中,添加如下依赖配置:

<dependency>
		<groupId>org.apache.hadoop</groupId>
		<artifactId>hadoop-client</artifactId>
		<version>2.8.1</version>
	</dependency>

方式2:直接使用本地jar包

从hadoop的安装目录中,拷贝所有jar包到项目工程中,并添加到buildpath即可,如图:

4c4d70f32258f8bcb726137494e6870c.png

图6.2 工程结构

6.1.5 Mapper开发

map函数的逻辑:

  1. 将map task所读取到的一行数据按照单词分隔符切分得到一个字符串数组;
  2. 遍历字符串数组;
  3. 对遍历到的每一个单词,通过map task所提供的context来返回一个<单词,1>的key-value数据对;

在工程中新建一个类WordCountMapper并继承Mapper,具体请看示例代码及代码注释:

public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
 
	/**
	 * maptask程序在自己的任务范围数据中(数据切片)
	 * 每读取一行数据,就调一次本map()方法,并将读到的数据以key-value形式作为参数传给本map()方法
	 * map()方法按照所需的数据处理逻辑对传入的数据进行处理,然后通过context返回给map task程序
	 */
	@Override
 	protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
		// key在默认情况下是maptask所读取到的一行文本的起始偏移量
		// value在默认情况下是maptask所读取到的一行文本的字符内容
 
		// 将文本按空格符号切分成单词数组
		String[] words = value.toString().split(" ");
		// 遍历数组中的每一个单词,将每个单词变成  (单词,1)这样的kv对数据,通过context返回给maptask
 		for (String word : words) {
			context.write(new Text(word), new IntWritable(1));
		}
	}
}

6.1.6 Reducer类开发

reduce函数的逻辑:

  1. Reduce Task会将自己所收到的key-value数据按照key进行排序和分组,相同key的数据会连续排放在一起,key“较小”的数据排在前面,然后,将key相同的key-value数据作为一组来进行聚合处理;
  2. Reduce Task对每一组数据处理时,都是调用用户提供的reduce方法,并传入一个key对象和一个迭代器对象给reduce方法来获取这一组数据中的每一个key-value数据;
  3. 在reduce函数中,我们只需要使用框架所提供的迭代器来迭代reduce task提供的一组key-value数据(具体来说,就是循环反复调用迭代器的next()方法来逐一获取这组数据中的每一对key-value数据);
  4. 将每次迭代到的value值进行累加,迭代完成之后,累加值即为某一个key(即某个单词)的总词频;
  5. 将统计出的这个单词及其总词频,通过reduce task所提供的context进行返回。

在工程中新建一个类WordCountReducer并继承Reducer,具体请看示例代码及注释:

public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
 
	/**
	 * Reduce Task收集到一组相同key的数据,调一次本方法,比如(a,1)(a,1)(a,1)......(a,1)
	 */
	@Override
 	protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
 
 		int count  = 0;
 
		// 每迭代一次,迭代器就会取一对key-value数据:
		// 取到的key数据重新设置给方法中的参数key
		// 取到的value数据则会设置给以下for循环中的临时变量value
 		for (IntWritable value : values) {
			// 将本次迭代到的value值累加到count变量
			count += value.get();
		}
 
		// 将这一组数据的聚合结果通过context返回给reduce task
		context.write(key, new IntWritable(count));
	}

}

6.1.7 job提交客户端开发

自定义Mapper类和自定义Reducer类开发好后,还需要开发一个程序,用于设置关于这个mapreduce job的相关参数,并将这些参数及整个程序jar包提交到Yarn上去启动执行。

在工程中新建一个类WordCountDriver类,并定义一个main方法,具体请看示例代码及注释:

public class WordcountDriver {
 
	public static void main(String[] args) throws Exception {
		// 需要将大量的跟jar包程序运行相关的参数进行描述
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);
 
		//job.setJar("/root/wc.jar");
		job.setJarByClass(WordcountDriver.class);
 
		// 指定本次job所要用的mapper类和reducer类
		job.setMapperClass(WordCountMapper.class);
		job.setCombinerClass(WordCountCombiner.class); 
		job.setReducerClass(WordCountReducer.class);
 
		// 指定本次job的mapper类和reducer类输出结果的数据类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(LongWritable.class);
 
		// 指定本次job读取源数据时所需要用的输入组件:源文件在hdfs的文本文件中,用TextInputFormat
		job.setInputFormatClass(TextInputFormat.class);
		// 指定本次job输出数据时所需要用的输出组件:输出到hdfs文本文件中,用TextOutputFormat
		job.setOutputFormatClass(TextOutputFormat.class);
 
		// 指定reduce task运行实例的个数
		job.setNumReduceTasks(3);
 
		// 指定源数据文件所在的路径
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		// 指定输出结果数据文件所在的路径
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
 
		// 核心代码: 提交jar包给yarn
		// job.submit();  // 提交完任务,客户端就退出了
		// 提交完任务,客户端打印集群中的运行进度信息,并等待最终运行状态
 		boolean res = job.waitForCompletion(true);  		
		System.exit(res?0:1); 
 
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值