hadoop中mapreduce的常用类

网络 网络管理 网络运维 Hadoop
新旧API是同时存在于1.1.2的hadoop中的。以前还一直纳闷儿为什么有时候是jobClient提交任务,有时是Job...不管API是否更新,下面这些类也还是存在于API中的,经过自己跟踪源码,发现原理还是这些。只不过进行了重新组织,进行了一些封装,使得扩展性更好。

写这个文章的时候才意识到新旧API是同时存在于1.1.2的hadoop中的。以前还一直纳闷儿为什么有时候是jobClient提交任务,有时是Job...不管API是否更新,下面这些类也还是存在于API中的,经过自己跟踪源码,发现原理还是这些。只不过进行了重新组织,进行了一些封装,使得扩展性更好。所以还是把这些东西从记事本贴进来吧。

关于这些类的介绍以及使用,有的是在自己debug中看到的,多数为纯翻译API的注释,但是翻译的过程受益良多。

GenericOptionsParser

parseGeneralOptions(Options opts, Configuration conf, String[] args)解析命令行参数

GenericOptionsParser是为hadoop框架解析命令行参数的工具类。它能够辨认标准的命令行参数,使app能够轻松指定namenode,jobtracker,以及额外的配置资源或信息等。它支持的功能有:

-conf 指定配置文件;

-D 指定配置信息;

-fs 指定namenode

-jt 指定jobtracker

-files 指定需要copy到MR集群的文件,以逗号分隔

-libjars指定需要copy到MR集群的classpath的jar包,以逗号分隔

-archives指定需要copy到MR集群的压缩文件,以逗号分隔,会自动解压缩

1. String[] otherArgs = new GenericOptionsParser(job, args)

2. .getRemainingArgs();

3. if (otherArgs.length != 2) {

4. System.err.println("Usage: wordcount ");

5. System.exit(2);

6. }

ToolRunner

用来跑实现Tool接口的工具。它与GenericOptionsParser合作来解析命令行参数,只在此次运行中更改configuration的参数。

Tool

处理命令行参数的接口。Tool是MR的任何tool/app的标准。这些实现应该代理对标准命令行参数的处理。下面是典型实现:

  1. public class MyApp extends Configured implements Tool {   
  2.            
  3. public int run(String[] args) throws Exception {   
  4. // 即将被ToolRunner执行的Configuration   
  5. Configuration conf = getConf();   
  6.             
  7. // 使用conf建立JobConf   
  8. JobConf job = new JobConf(conf, MyApp.class);   
  9.         
  10. // 执行客户端参数   
  11. Path in = new Path(args[1]);   
  12. Path out = new Path(args[2]);   
  13.             
  14. // 指定job相关的参数        
  15. job.setJobName("my-app");   
  16. job.setInputPath(in);   
  17. job.setOutputPath(out);   
  18. job.setMapperClass(MyApp.MyMapper.class);   
  19. job.setReducerClass(MyApp.MyReducer.class);   
  20. *   
  21. // 提交job,然后监视进度直到job完成   
  22. JobClient.runJob(job);   
  23. }   
  24.           
  25.  public static void main(String[] args) throws Exception {   
  26. // 让ToolRunner 处理命令行参数    
  27. int res = ToolRunner.run(new Configuration(), new Sort(), //这里封装了GenericOptionsParser解析args   
  28.             
  29. System.exit(res);   
  30. }   
  31. }   

MultipleOutputFormat

自定义输出文件名称或者说名称格式。在jobconf中setOutputFormat(MultipleOutputFormat的子类)就行了。而不是那种part-r-00000啥的了。。。并且可以分配结果到多个文件中。

MultipleOutputFormat继承了FileOutputFormat, 允许将输出数据写进不同的输出文件中。有三种应用场景:

a. 最少有一个reducer的mapreduce任务。这个reducer想要根据实际的key将输出写进不同的文件中。假设一个key编码了实际的key和为实际的key指定的位置

b. 只有map的任务。这个任务想要把输入文件或者输入内容的部分名称设为输出文件名。

c. 只有map的任务。这个任务为输出命名时,需要依赖keys和输入文件名。 

  1. //这里是根据key生成多个文件的地方,可以看到还有value,name等参数   
  2. @Override   
  3. protected String generateFileNameForKeyValue(Text key,   
  4. IntWritable value, String name) {   
  5. char c = key.toString().toLowerCase().charAt(0);   
  6. if (c >= 'a' && c <= 'z') {   
  7. return c + ".txt";   
  8. }   
  9. return "result.txt";   
  10. }   

DistributedCache

在集群中快速分发大的只读文件。DistributedCache是MR用来缓存app需要的诸如text,archive,jar等的文件的。app通过jobconf中的url来指定需要缓存的文件。它会假定指定的这个文件已经在url指定的对应位置上了。在job在node上执行之前,DistributedCache会copy必要的文件到这个slave node。它的功效就是为每个job只copy一次,而且copy到指定位置,能够自动解压缩。

DistributedCache可以用来分发简单的只读文件,或者一些复杂的例如archive,jar文件等。archive文件会自动解压缩,而jar文件会被自动放置到任务的classpath中(lib)。分发压缩archive时,可以指定解压名称如:dict.zip#dict。这样就会解压到dict中,否则默认是dict.zip中。

文件是有执行权限的。用户可以选择在任务的工作目录下建立指向DistributedCache的软链接。

  1. DistributedCache.createSymlink(conf);     
  2. DistributedCache.addCacheFile(new Path("hdfs://host:port/absolute-path#link-name").toUri(), conf);      

DistributedCache.createSymlink(Configuration)方法让DistributedCache 在当前工作目录下创建到缓存文件的符号链接。则在task的当前工作目录会有link-name的链接,相当于快捷方法,链接到expr.txt文件,在setup方法使用的情况则要简单许多。或者通过设置配置文件属性mapred.create.symlink为yes。 分布式缓存会截取URI的片段作为链接的名字。 例如,URI是 hdfs://namenode:port/lib.so.1#lib.so, 则在task当前工作目录会有名为lib.so的链接, 它会链接分布式缓存中的lib.so.1#p#

DistributedCache会跟踪修改缓存文件的timestamp。

下面是使用的例子, 为应用app设置缓存

  1. $ bin/hadoop fs -copyFromLocal lookup.dat /myapp/lookup.dat     
  2. $ bin/hadoop fs -copyFromLocal map.zip /myapp/map.zip     
  3. $ bin/hadoop fs -copyFromLocal mylib.jar /myapp/mylib.jar   
  4. $ bin/hadoop fs -copyFromLocal mytar.tar /myapp/mytar.tar   
  5. $ bin/hadoop fs -copyFromLocal mytgz.tgz /myapp/mytgz.tgz   
  6. $ bin/hadoop fs -copyFromLocal mytargz.tar.gz /myapp/mytargz.tar.gz   

2. 设置app的jobConf:

  1. JobConf job = new JobConf();   
  2. DistributedCache.addCacheFile(new URI("/myapp/lookup.dat#lookup.dat"),    
  3.  job);   
  4. DistributedCache.addCacheArchive(new URI("/myapp/map.zip", job);  
  5. DistributedCache.addFileToClassPath(new Path("/myapp/mylib.jar"), job);   
  6. DistributedCache.addCacheArchive(new URI("/myapp/mytar.tar", job);   
  7. DistributedCache.addCacheArchive(new URI("/myapp/mytgz.tgz", job);   
  8. DistributedCache.addCacheArchive(new URI("/myapp/mytargz.tar.gz", job);   

3. 在mapper或者reducer中使用缓存文件:

  1. public static class MapClass extends MapReduceBase     
  2. implements Mapper<K, V, K, V> {   
  3.      
  4. private Path[] localArchives;   
  5. private Path[] localFiles;   
  6.            
  7. public void configure(JobConf job) {   
  8. // 得到刚刚缓存的文件   
  9. localArchives = DistributedCache.getLocalCacheArchives(job);   
  10. localFiles = DistributedCache.getLocalCacheFiles(job);   
  11. }   
  12.            
  13. public void map(K key, V value,    
  14.  OutputCollector<K, V>; output, Reporter reporter)    
  15. throws IOException {   
  16. // 使用缓存文件   
  17. // ...   
  18. // ...   
  19. output.collect(k, v);   
  20. }   
  21. }   

它跟GenericOptionsParser的部分功能有异曲同工之妙。

PathFilter + 通配符。accept(Path path)筛选path是否通过。

NullWritable

不想输出的时候,把它当做key。NullWritable是Writable的一个特殊类,序列化的长度为0,实现方法为空实现,不从数据流中读数据,也不写入数据,只充当占位符,如在MapReduce中,如果你不需要使用键或值,你就可以将键或值声明为NullWritable,NullWritable是一个不可变的单实例类型。

FileInputFormat继承于InputFormat

InputFormat的作用:

验证输入规范;

切分输入文件为InputSpilts;

提供RecordReader来收集InputSplit中的输入记录,给Mapper进行执行。

RecordReader

将面向字节的InputSplit转换为面向记录的视图,供Mapper或者Reducer使用运行。因此假定处理记录的责任界限,为任务呈现key-value。

SequenceFile:

SequenceFile是包含二进制kv的扁平文件(序列化)。它提供Writer、Reader、Sorter来进行写、读、排序功能。基于CompressionType,SequenceFile有三种对于kv的压缩方式:

●Writer:不压缩records;

RecordCompressWriter: 只压缩values;

BlockCompressWriter: 压缩records,keys和values都被分开压缩在block中,block的大小可以配置;

压缩方式由合适的CompressionCodec指定。推荐使用此类的静态方法createWriter来选择格式。Reader作为桥接可以读取以上任何一种压缩格式。

CompressionCodec:

封装了关于流式压缩/解压缩的相关方法。

Mapper

Mapper 将输入的kv对映射成中间数据kv对集合。Maps 将输入记录转变为中间记录,其中被转化后的记录不必和输入记录类型相同。一个给定的输入对可以映射为0或者多个输出对。

在MRJob执行过程中,MapReduce框架根据提前指定的InputFormat(输入格式对象)产生InputSplit(输入分片),而每个InputSplit将会由一个map任务处理。

总起来讲,Mapper实现类通过JobConfigurable.configure(JobConf)方法传入JobConf对象来初始化,然后在每个map任务中调用map(WritableComparable,Writable,OutputCollector,Reporter)方法处理InputSplit的每个kv对。MR应用可以覆盖Closeable.close方法去处理一些必须的清理工作。

输出对不一定和输入对类型相同。一个给定的输入对可能映射成0或者很多的输出对。输出对是框架通过调用OutputCollector.colect(WritableComparable,Writable)得到。

MR应用可以使用Reporter汇报进度,设置应用层级的状态信息,更新计数器或者只是显示应用处于运行状态等。

所有和给定的输出key关联的中间数据都会随后被框架分组处理,并传给Reducer处理以产生最终的输出。用户可以通过JobConf.setOutputKeyComparatorClass(Class)指定一个Comparator控制分组处理过程。

Mapper输出都被排序后根据Reducer数量进行分区,分区数量等于reduce任务数量。用户可以通过实现自定义的Partitioner来控制哪些keys(记录)到哪个Reducer中去。

此外,用户还可以指定一个Combiner,调用JobConf.setCombinerClass(Class)来实现。这个可以来对map输出做本地的聚合,有助于减少从mapper到reducer的数据量。

经过排序的中间输出数据通常以一种简单的格式(key-len,key,value-len,value)存储在SequenceFile中。应用可以决定是否或者怎样被压缩以及压缩格式,可以通过JobConf来指定CompressionCodec.

如果job没有reducer,那么mapper的输出结果会不经过分组排序,直接写进FileSystem.#p#

Map数

通常map数由输入数据总大小决定,也就是所有输入文件的blocks数目决定。

每个节点并行的运行的map数正常在10到100个。由于Map任务初始化本身需要一段时间所以map运行时间至少在1分钟为好。

如此,如果有10T的数据文件,每个block大小128M,***使用为82000map数,除非使用setNumMapTasks(int)(这个方法仅仅对MR框架提供一个建议值)将map数值设置到更高。

Reducer

Reducer根据key将中间数据集合处理合并为更小的数据结果集。

用户可以通过JobConf.setNumReduceTasks(int)设置作业的reducer数目。

整体而言,Reducer实现类通过JobConfigurable.configure(JobConf)方法将JobConf对象传入,并为Job设置和初始化Reducer。MR框架调用 reduce(WritableComparable, Iterator, OutputCollector, Reporter) 来处理以key被分组的输入数据。应用可以覆盖Closeable.close()处理必要的清理操作。

Reducer由三个主要阶段组成:shuffle,sort,reduce。

❈shuffle

 

输入到Reducer的输入数据是Mapper已经排过序的数据.在shuffle阶段,根据partition算法获取相关的mapper地址,并通过Http协议将mapper的相应输出数据由reducer拉取到reducer机器上处理。

sort

 

框架在这个阶段会根据key对reducer的输入进行分组(因为不同的mapper输出的数据中可能含有相同的key)。

shuffle和sort是同时进行的,同时reducer仍然在拉取map的输出。

Secondary Sort

 

如果对中间数据key进行分组的规则和在处理化简阶段前对key分组规则不一致时,可以通过 JobConf.setOutputValueGroupingComparator(Class)设置一个Comparator。因为中间数据的分组策略是通过 JobConf.setOutputKeyComparatorClass(Class) 设置的,可以控制中间数据根据哪些key进行分组。而JobConf.setOutputValueGroupingComparator(Class)则可用于在数据连接情况下对value进行二次排序。

Reduce(化简)

这个阶段框架循环调用 reduce(WritableComparable, Iterator, OutputCollector, Reporter) 方法处理被分组的每个kv对。

reduce 任务一般通过 OutputCollector.collect(WritableComparable, Writable)将输出数据写入文件系统FileSystem。应用可以使用Reporter汇报作业执行进度、设置应用层级的状态信息并更新计数器(Counter),或者只是提示作业在运行。

注意,Reducer的输出不会再进行排序。

Reducer数目

合适的reducer数目可以这样估算:(节点数目mapred.tasktracker.reduce.tasks.maximum)乘以0.95 或 乘以1.75。因子为0.95时,当所有map任务完成时所有reducer可以立即启动,并开始从map机器上拉取数据。因子为1.75时,最快的一些节点将完成***轮reduce处理,此时框架开始启动第二轮reduce任务,这样可以达到比较好的作业负载均衡。提高reduce数目会增加框架的运行负担,但有利于提升作业的负载均衡并降低失败的成本。上述的因子使用***在作业执行时框架仍然有reduce槽为前提,毕竟框架还需要对作业进行可能的推测执行和失败任务的处理。

不使用Reducer

如果不需要进行化简处理,可以将reduce数目设为0。这种情况下,map的输出会直接写入到文件系统。输出路径通过setOutputPath(Path)指定。框架在写入数据到文件系统之前不再对map结果进行排序。

Partitioner

Partitioner对数据按照key进行分区,从而控制map的输出传输到哪个reducer上。默认的Partitioner算法是hash(哈希。分区数目由作业的reducer数目决定。HashPartitioner 是默认的Partitioner。

Reporter

Reporter为MR应用提供了进度报告、应用状态信息设置,和计数器(Counter)更新等功能.

Mapper和Reducer实现可以使用Reporter汇报进度或者提示作业在正常运行。在一些场景下,应用在处理一些特殊的kv对时耗费了过多时间,这个可能会因为框架假定任务超时而强制停止了这些作业。为避免该情况,可以设置mapred.task.timeout 为一个比较高的值或者将其设置为0以避免超时发生。

应用也可以使用Reporter来更新计数(Counter)。

OutputCollector

OutputCollector是MR框架提供的通用工具来收集Mapper或者Reducer输出数据(中间数据或者最终结果数据)。

Hadoop MapReduce提供了一些经常使用的mapper、reducer和partioner的实现类供我们进行学习。

 

以上有关configuration和job的部分在新的API中有所改变,简单说就是在Mapper和Reducer中引入了MapContext和ReduceContext,它们封装了configuration和outputcollector,以及reporter。

责任编辑:守望幸福 来源: 51CTO.com
相关推荐

2017-04-19 11:17:48

SparkHadoopMapReduce

2010-06-07 13:35:16

Hadoop简介

2010-06-03 16:32:09

Hadoop MapR

2014-12-29 09:59:03

Spark 1.2MapReduce

2010-06-03 16:18:07

Hadoop MapR

2013-11-27 09:21:18

YARNMapReduceHadoop

2013-04-24 10:47:48

Hadoop集群

2013-01-21 13:22:56

IBMdW

2014-11-10 15:02:21

大数据云计算Hadoop

2019-10-31 09:52:02

HadoopJava大数据

2012-04-23 10:30:38

Hadoop

2014-10-15 16:32:43

MapReducehadoop

2010-06-07 11:12:52

Hadoop-0.20

2013-12-17 09:52:13

pythonhadoopmapreduce

2014-01-07 14:04:13

HadoopMapReduce

2010-06-07 11:30:24

Hadoop源代码

2012-05-09 09:13:29

IDCHadoopMapReduce

2014-03-18 10:19:55

Hadoop部署hadoop集群脚本

2015-07-01 13:51:12

HadoopMapReduce数据分析

2014-01-07 15:23:15

HadoopYARN
点赞
收藏

51CTO技术栈公众号