MapReduce编程实战之“I/O”

本篇通过对MapReduce模型的分析,加深对MapReduce模型的了解;并介绍MapReduc编程模型的常用输入格式和输出格式,在这些常用格式之外,我们可以扩展自己的输入格式,比如:如果我们需要把Mongo数据作为输入,可以通过扩展InputFormat、InputSplit的方式实现。


MapReduce模型深入了解


我们已经知道:map和reduce函数的输入和输出是键值对,下面,我们开始先对这个模型进行深入了解。首先,分析一个默认的MapReduce作业程序。


(1)一个最简单的MapReduce程序

[java] view plain copy print ?
  1. import org.apache.hadoop.conf.Configured;  
  2. import org.apache.hadoop.fs.Path;  
  3. import org.apache.hadoop.mapred.FileInputFormat;  
  4. import org.apache.hadoop.mapred.FileOutputFormat;  
  5. import org.apache.hadoop.mapred.JobClient;  
  6. import org.apache.hadoop.mapred.JobConf;  
  7. import org.apache.hadoop.util.Tool;  
  8. import org.apache.hadoop.util.ToolRunner;  
  9.   
  10. public class MinimalMapReduce extends Configured implements Tool {  
  11.   
  12.     @Override  
  13.     public int run(String[] args) throws Exception {  
  14.         JobConf conf = new JobConf(getConf(), getClass());  
  15.         FileInputFormat.addInputPath(conf, new Path("/test/input/t"));  
  16.         FileOutputFormat.setOutputPath(conf, new Path("/test/output/t"));  
  17.         JobClient.runJob(conf);  
  18.         return 0;  
  19.     }  
  20.     public static void main(String[] args) throws Exception {  
  21.         int exitCode = ToolRunner.run(new MinimalMapReduce(), args);  
  22.         System.exit(exitCode);  
  23.     }  
  24. }  

(2)功能同上,默认值显示设置
[java] view plain copy print ?
  1. import org.apache.hadoop.conf.Configured;  
  2. import org.apache.hadoop.fs.Path;  
  3. import org.apache.hadoop.io.LongWritable;  
  4. import org.apache.hadoop.io.Text;  
  5. import org.apache.hadoop.mapred.FileInputFormat;  
  6. import org.apache.hadoop.mapred.FileOutputFormat;  
  7. import org.apache.hadoop.mapred.JobClient;  
  8. import org.apache.hadoop.mapred.JobConf;  
  9. import org.apache.hadoop.mapred.MapRunner;  
  10. import org.apache.hadoop.mapred.TextInputFormat;  
  11. import org.apache.hadoop.mapred.TextOutputFormat;  
  12. import org.apache.hadoop.mapred.lib.HashPartitioner;  
  13. import org.apache.hadoop.mapred.lib.IdentityMapper;  
  14. import org.apache.hadoop.mapred.lib.IdentityReducer;  
  15. import org.apache.hadoop.util.Tool;  
  16. import org.apache.hadoop.util.ToolRunner;  
  17.   
  18.   
  19. public class MinimalMapReduceWithDefaults extends Configured implements Tool {  
  20.   
  21.     @Override  
  22.     public int run(String[] args) throws Exception {  
  23.         JobConf conf = new JobConf(getConf(), getClass());  
  24.         FileInputFormat.addInputPath(conf, new Path("/test/input/t"));  
  25.         FileOutputFormat.setOutputPath(conf, new Path("/test/output/t"));  
  26.           
  27.         conf.setInputFormat(TextInputFormat.class);  
  28.           
  29.         conf.setNumMapTasks(1);  
  30.         conf.setMapperClass(IdentityMapper.class);  
  31.         conf.setMapRunnerClass(MapRunner.class);  
  32.           
  33.         conf.setMapOutputKeyClass(LongWritable.class);  
  34.         conf.setMapOutputValueClass(Text.class);  
  35.           
  36.         conf.setPartitionerClass(HashPartitioner.class);  
  37.           
  38.         conf.setNumReduceTasks(1);  
  39.         conf.setReducerClass(IdentityReducer.class);  
  40.           
  41.         conf.setOutputKeyClass(LongWritable.class);  
  42.         conf.setOutputValueClass(Text.class);  
  43.           
  44.         conf.setOutputFormat(TextOutputFormat.class);  
  45.           
  46.         JobClient.runJob(conf);  
  47.         return 0;  
  48.     }  
  49.     public static void main(String[] args) throws Exception {  
  50.         int exitCode = ToolRunner.run(new MinimalMapReduceWithDefaults(), args);  
  51.         System.exit(exitCode);  
  52.     }  
  53. }  


输入分片


一个输入分片(split)就是由单个map处理的输入块。

MapReduce应用开发人员不需要直接处理InputSplit,因为它是由InputFormat创建的。InputFormat 负责产生输入分片并将它们分割成记录。


如何控制分片的大小


MapReduce编程实战之“I/O”_第1张图片


避免切分


[java] view plain copy print ?
  1. import org.apache.hadoop.fs.FileSystem;  
  2. import org.apache.hadoop.fs.Path;  
  3. import org.apache.hadoop.mapred.TextInputFormat;  
  4.   
  5. public class NoSplittableTextInputFormat extends TextInputFormat {  
  6.       
  7.     @Override  
  8.     protected boolean isSplitable(FileSystem fs,Path file)  
  9.     {  
  10.         return false;  
  11.     }  
  12. }  

把整个文件作为一条记录处理


[java] view plain copy print ?
  1. import java.io.IOException;  
  2. import org.apache.hadoop.conf.Configuration;  
  3. import org.apache.hadoop.fs.FSDataInputStream;  
  4. import org.apache.hadoop.fs.FileSystem;  
  5. import org.apache.hadoop.fs.Path;  
  6. import org.apache.hadoop.io.BytesWritable;  
  7. import org.apache.hadoop.io.IOUtils;  
  8. import org.apache.hadoop.io.NullWritable;  
  9. import org.apache.hadoop.mapred.FileInputFormat;  
  10. import org.apache.hadoop.mapred.FileSplit;  
  11. import org.apache.hadoop.mapred.InputSplit;  
  12. import org.apache.hadoop.mapred.JobConf;  
  13. import org.apache.hadoop.mapred.RecordReader;  
  14. import org.apache.hadoop.mapred.Reporter;  
  15.   
  16. public class WholeFileInputFormat extends  
  17.         FileInputFormat<NullWritable, BytesWritable> {  
  18.   
  19.     @Override  
  20.     protected boolean isSplitable(FileSystem fs, Path file) {  
  21.         return false;  
  22.     }  
  23.   
  24.     @Override  
  25.     public RecordReader<NullWritable, BytesWritable> getRecordReader(  
  26.             InputSplit split, JobConf job, Reporter reporter)  
  27.             throws IOException {  
  28.         return new WholeFileRecordReader((FileSplit) split, job);  
  29.     }  
  30. }  
  31.   
  32. class WholeFileRecordReader implements  
  33.         RecordReader<NullWritable, BytesWritable> {  
  34.     private FileSplit fileSplit;  
  35.     private Configuration conf;  
  36.     private boolean processed = false;  
  37.   
  38.     public WholeFileRecordReader(FileSplit fileSplit, Configuration conf) {  
  39.         this.fileSplit = fileSplit;  
  40.         this.conf = conf;  
  41.     }  
  42.   
  43.     @Override  
  44.     public void close() throws IOException {  
  45.     }  
  46.   
  47.     @Override  
  48.     public NullWritable createKey() {  
  49.         return NullWritable.get();  
  50.     }  
  51.   
  52.     @Override  
  53.     public BytesWritable createValue() {  
  54.         return new BytesWritable();  
  55.     }  
  56.   
  57.     @Override  
  58.     public long getPos() throws IOException {  
  59.         return processed ? fileSplit.getLength() : 0;  
  60.     }  
  61.   
  62.     @Override  
  63.     public float getProgress() throws IOException {  
  64.         return processed ? 1.0f : 0.0f;  
  65.     }  
  66.   
  67.     @Override  
  68.     public boolean next(NullWritable key, BytesWritable value)  
  69.             throws IOException {  
  70.         if (!processed) {  
  71.             byte[] contents = new byte[(int) fileSplit.getLength()];  
  72.             Path file = fileSplit.getPath();  
  73.             FileSystem fs = file.getFileSystem(conf);  
  74.             FSDataInputStream in = null;  
  75.             try {  
  76.                 in = fs.open(file);  
  77.                 IOUtils.readFully(in, contents, 0, contents.length);  
  78.                 value.set(contents, 0, contents.length);  
  79.             } finally {  
  80.                 IOUtils.closeStream(in);  
  81.             }  
  82.             processed = true;  
  83.             return true;  
  84.         }  
  85.         return false;  
  86.     }  
  87. }  

输入格式


InputFormat类的层次结构


MapReduce编程实战之“I/O”_第2张图片


FileInputFormat类


FileInputFormat是所有使用文件作为数据源的InputFormat实现的基类,它提供了两个功能:一个定义哪些文件包含在一个作业的输入中;一个为输入文件生成分片的实现。把分片切割成记录的作业由其子类来完成。


TextInputFormat


TextInputFormat是默认的InputFormat。每条记录是一行输入。键是LongWritable类型,存储该行在整个文件中的字节偏移量。值是这行的内容,不包括终止符(换行符和回车符),它是Text类型的。


KeyValueTextInputFormat


通常情况下,文件张的每一行是一个键值对,使用某个分隔符进行分隔,比如制表符。可以通过key.value.separator.in.input.line属性来指定分隔符,它的默认值是一个制表符。


NLineInputFormat


如果希望Map收到固定行数的输入,需要使用NLineInputFormat。与 TextInputFormat一样,键是文件中 行的字节偏移量,值是行本身。mapred.line.input.format.linespermap属性控制N的值,默认是1。


二进制输入


SequenceFileInputFormat、SequenceFileAsTextInputFormat、SequenceFileAsBinaryInputFormat。


多种输入


多个输入,对于每个输入指定一个Mapper,当然,也可以多种输入格式而只有一个Mapper。


输出格式


OutputFormat类的层次结构


MapReduce编程实战之“I/O”_第3张图片


和输入对应,输出大约有如下几种类型:

文本输出、二进制输出、多个输出、延迟输出,数据库输出。

你可能感兴趣的:(MapReduce编程实战之“I/O”)