Hadoop 自定义InputFormat实现自定义Split

来源:互联网 发布:os x 安装软件 编辑:程序博客网 时间:2024/05/29 06:40

原文链接:http://blog.csdn.net/anbo724/article/details/6956286

上一篇文章中提到了如何进行RecordReader的重写,本篇文章就是来实现如何实现自定义split的大小

要解决的需求:

(1)一个文本中每一行都记录了一个文件的路径,

(2)要求处理路径对应的文件,但是因为文件量比较大,所以想进行分布式处理

(3)所以就对输入的文档进行预处理,读取前N行做为一个splits,但是没有实现,因为重写FileSplit不是太容易实现,就偷懒直接定义一个split的大小是1000个字节,这样就可以将输入的文档进行分片了。

直接贴代码:

InputFormat

[java] view plaincopy
  1. /** 
  2. * @file LineInputFormat.java 
  3. * @brief自定义InputFormat 实现split大小的控制 
  4. * @author anbo, anbo724@gmail.com 
  5. * @version 1.0 
  6. * @date 2011-10-18 
  7. */  
  8. /* Copyright(C) 
  9. * For free 
  10. * All right reserved 
  11. * 
  12. */   
  13.   
  14.   
  15. package an.hadoop.test;  
  16.   
  17.   
  18. import java.io.IOException;  
  19. import java.util.ArrayList;  
  20. import java.util.List;  
  21.   
  22. import org.apache.commons.logging.Log;   
  23. import org.apache.commons.logging.LogFactory;  
  24. import org.apache.hadoop.fs.BlockLocation;  
  25. import org.apache.hadoop.fs.FileStatus;  
  26. import org.apache.hadoop.fs.FileSystem;  
  27. import org.apache.hadoop.fs.Path;  
  28. import org.apache.hadoop.io.LongWritable;  
  29. import org.apache.hadoop.io.Text;  
  30. import org.apache.hadoop.io.compress.CompressionCodec;  
  31. import org.apache.hadoop.io.compress.CompressionCodecFactory;  
  32. import org.apache.hadoop.mapreduce.InputFormat;  
  33. import org.apache.hadoop.mapreduce.InputSplit;  
  34. import org.apache.hadoop.mapreduce.JobContext;  
  35. import org.apache.hadoop.mapreduce.RecordReader;  
  36. import org.apache.hadoop.mapreduce.TaskAttemptContext;  
  37. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  38. import org.apache.hadoop.mapreduce.lib.input.FileSplit;  
  39. import org.apache.hadoop.mapreduce.lib.input.LineRecordReader;  
  40.   
  41. public class LineInputFormat extends FileInputFormat<LongWritable , Text> {  
  42.       
  43.     public long mySplitSize = 1000;  
  44.       
  45.      private static final Log LOG = LogFactory.getLog(FileInputFormat.class);  
  46.   
  47.       private static final double SPLIT_SLOP = 1.1;   // 10% slop  
  48.   
  49.      @Override  
  50.       public RecordReader<LongWritable, Text>   
  51.         createRecordReader(InputSplit split,  
  52.                            TaskAttemptContext context) {  
  53.         return new LineRecordReader(); //为什么不行呢   
  54.       }  
  55.       
  56.     @Override  
  57.     protected boolean isSplitable(JobContext context, Path file) {  
  58.         CompressionCodec codec =  
  59.         new CompressionCodecFactory(context.getConfiguration()).getCodec(file);  
  60.         //return codec == null;  
  61.         return true;//要求分片  
  62.     }  
  63.       
  64.      /**  
  65.        * Generate the list of files and make them into FileSplits. 
  66.        */   
  67.     @Override  
  68.       public List<InputSplit> getSplits(JobContext job) throws IOException {  
  69.         long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));  
  70.         long maxSize = getMaxSplitSize(job);  
  71.   
  72.         // generate splits  
  73.         List<InputSplit> splits = new ArrayList<InputSplit>(); //用以存放生成的split的    
  74.         for (FileStatus file: listStatus(job)) {//filestatues是文件对应的信息,具体看对应的类  
  75.           Path path = file.getPath();  
  76.           FileSystem fs = path.getFileSystem(job.getConfiguration());  
  77.           long length = file.getLen(); //得到文本的长度  
  78.           BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length); //取得文件所在块的位置  
  79.           if ((length != 0) && isSplitable(job, path)) { //如果文件不为空,并且可以分片的话就进行下列操作,  
  80.             long blockSize = file.getBlockSize();//  
  81.             //long splitSize = computeSplitSize(blockSize, minSize, maxSize); //split的大小Math.max(minSize, Math.min(maxSize, blockSize));  
  82.             //可以通过调整splitSize的大小来控制对应的文件块的大小,比如设置splitSize=100,那么就可以控制成每个split的大小  
  83.             //但是问题是,我是要求按行进行处理的,虽然这样应该也可以按行进行切分了,不过却不能保证每个split对应的行数都是相等的  
  84.             //一般情况是如果文件大于64M(32M)就会使用块大小来作为split  
  85.             long splitSize = mySplitSize;  
  86.             long bytesRemaining = length; //文本的长度  
  87.               
  88.             while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {//剩下的文本长度大于split大小的SPLIT_SLOP倍数  
  89.               int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);//找到对应block块中对应的第0个字符开始,  
  90.               splits.add(new FileSplit(path, length-bytesRemaining, splitSize,    
  91.                                        blkLocations[blkIndex].getHosts()));   
  92.             //这个是形成split的代码FileSplit(文件路径,0,split大小,host)  
  93.               //原始函数为 FileSplit(Path file, long start, long length, String[] hosts) {  
  94.               //但是应该可以通过重写FileSplit来实现对应的要求  
  95.               bytesRemaining -= splitSize;  
  96.             }  
  97.               
  98.             if (bytesRemaining != 0) {  
  99.               splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,   
  100.                          blkLocations[blkLocations.length-1].getHosts()));  
  101.             }  
  102.           } else if (length != 0) {  
  103.             splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts()));  
  104.           } else {   
  105.             //Create empty hosts array for zero length files  
  106.             splits.add(new FileSplit(path, 0, length, new String[0]));  
  107.           }  
  108.         }  
  109.         LOG.debug("Total # of splits: " + splits.size());  
  110.         return splits;  
  111.       }  
  112.   
  113.       
  114.   
  115.       
  116.       
  117.       
  118.   
  119. }  
main类


[java] view plaincopy
  1. public class Test_multi {  
  2.       
  3.     public static void main(String[] args) throws Exception {  
  4.         Configuration conf = new Configuration();  
  5.         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();  
  6.         if (otherArgs.length != 2) {  
  7.           System.err.println("Usage: test_multi <in> <out>");  
  8.           System.exit(2);  
  9.         }  
  10.         Job job = new Job(conf, "test_multi");  
  11.         job.setJarByClass(Test_multi.class);  
  12.         job.setMapperClass(MultiMapper.class);  
  13.        // job.setInputFormatClass(LineInputFormat.class);//自定义了InputFormat  
  14.         //job.setCombinerClass(IntSumReducer.class);  
  15.         //job.setReducerClass(IntSumReducer.class);  
  16.         job.setOutputKeyClass(Text.class);  
  17.         job.setOutputValueClass(Text.class);  
  18.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  19.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  20.         //job.waitForCompletion(true);  
  21.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  22.       }  

然后看下一日志;

不使用自定义的InputFormat的处理结果是

[java] view plaincopy
  1. 11/11/10 14:54:25 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=  
  2. 11/11/10 14:54:25 WARN mapred.JobClient: No job jar file set.  User classes may not be found. See JobConf(Class) or JobConf#setJar(String).  
  3. 11/11/10 14:54:25 INFO input.FileInputFormat: Total input paths to process : 1  
  4. 11/11/10 14:54:25 INFO mapred.JobClient: Running job: job_local_0001  
  5. 11/11/10 14:54:25 INFO input.FileInputFormat: Total input paths to process : 1  
  6. 11/11/10 14:54:26 INFO mapred.MapTask: io.sort.mb = 100  
  7. 11/11/10 14:54:26 INFO mapred.JobClient:  map 0% reduce 0%  
  8. 11/11/10 14:54:26 INFO mapred.MapTask: data buffer = 79691776/99614720  
  9. 11/11/10 14:54:26 INFO mapred.MapTask: record buffer = 262144/327680  
  10. 11/11/10 14:54:32 INFO mapred.LocalJobRunner:   
  11. 11/11/10 14:54:33 INFO mapred.JobClient:  map 58% reduce 0%  
  12. 11/11/10 14:54:34 INFO mapred.MapTask: Starting flush of map output  
  13. 11/11/10 14:54:35 INFO mapred.LocalJobRunner:   
  14. 11/11/10 14:54:35 INFO mapred.JobClient:  map 100% reduce 0%  
  15. 11/11/10 14:54:35 INFO mapred.MapTask: Finished spill 0  
  16. 11/11/10 14:54:35 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting  
  17. 11/11/10 14:54:35 INFO mapred.LocalJobRunner:   
  18. 11/11/10 14:54:35 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000000_0' done.  
  19. 11/11/10 14:54:35 INFO mapred.LocalJobRunner:   
  20. 11/11/10 14:54:35 INFO mapred.Merger: Merging 1 sorted segments  
  21. 11/11/10 14:54:35 INFO mapred.Merger: Down to the last merge-pass, with 1 segments left of total size: 2974 bytes  
  22. 11/11/10 14:54:35 INFO mapred.LocalJobRunner:   
  23. 11/11/10 14:54:36 INFO mapred.TaskRunner: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting  
  24. 11/11/10 14:54:36 INFO mapred.LocalJobRunner:   
  25. 11/11/10 14:54:36 INFO mapred.TaskRunner: Task attempt_local_0001_r_000000_0 is allowed to commit now  
  26. 11/11/10 14:54:36 INFO output.FileOutputCommitter: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://an.local:9100/user/an/out2  
  27. 11/11/10 14:54:36 INFO mapred.LocalJobRunner: reduce > reduce  
  28. 11/11/10 14:54:36 INFO mapred.TaskRunner: Task 'attempt_local_0001_r_000000_0' done.  
  29. 11/11/10 14:54:36 INFO mapred.JobClient:  map 100% reduce 100%  
  30. 11/11/10 14:54:36 INFO mapred.JobClient: Job complete: job_local_0001  
  31. 11/11/10 14:54:36 INFO mapred.JobClient: Counters: 14  
  32. 11/11/10 14:54:36 INFO mapred.JobClient:   FileSystemCounters  
  33. 11/11/10 14:54:36 INFO mapred.JobClient:     FILE_BYTES_READ=35990  
  34. 11/11/10 14:54:36 INFO mapred.JobClient:     HDFS_BYTES_READ=8052  
  35. 11/11/10 14:54:36 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=72570  
  36. 11/11/10 14:54:36 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=2642  
  37. 11/11/10 14:54:36 INFO mapred.JobClient:   Map-Reduce Framework  
  38. 11/11/10 14:54:36 INFO mapred.JobClient:     Reduce input groups=165  
  39. 11/11/10 14:54:36 INFO mapred.JobClient:     Combine output records=0  
  40. 11/11/10 14:54:36 INFO mapred.JobClient:     Map input records=165  
  41. 11/11/10 14:54:36 INFO mapred.JobClient:     Reduce shuffle bytes=0  
  42. 11/11/10 14:54:36 INFO mapred.JobClient:     Reduce output records=165  
  43. 11/11/10 14:54:36 INFO mapred.JobClient:     Spilled Records=330  
  44. 11/11/10 14:54:36 INFO mapred.JobClient:     Map output bytes=2642  
  45. 11/11/10 14:54:36 INFO mapred.JobClient:     Combine input records=0  
  46. 11/11/10 14:54:36 INFO mapred.JobClient:     Map output records=165  
  47. 11/11/10 14:54:36 INFO mapred.JobClient:     Reduce input records=165  

使用自定义的InputFormat的日志是:

[java] view plaincopy
  1. 11/11/10 14:42:41 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=  
  2. 11/11/10 14:42:41 WARN mapred.JobClient: No job jar file set.  User classes may not be found. See JobConf(Class) or JobConf#setJar(String).  
  3. 11/11/10 14:42:41 INFO input.FileInputFormat: Total input paths to process : 1  
  4. 11/11/10 14:42:42 INFO mapred.JobClient: Running job: job_local_0001  
  5. 11/11/10 14:42:42 INFO input.FileInputFormat: Total input paths to process : 1  
  6. 11/11/10 14:42:42 INFO mapred.MapTask: io.sort.mb = 100  
  7. 11/11/10 14:42:43 INFO mapred.JobClient:  map 0% reduce 0%  
  8. 11/11/10 14:42:46 INFO mapred.MapTask: data buffer = 79691776/99614720  
  9. 11/11/10 14:42:46 INFO mapred.MapTask: record buffer = 262144/327680  
  10. 11/11/10 14:42:49 INFO mapred.MapTask: Starting flush of map output  
  11. 11/11/10 14:42:49 INFO mapred.MapTask: Finished spill 0  
  12. 11/11/10 14:42:49 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting  
  13. 11/11/10 14:42:49 INFO mapred.LocalJobRunner:   
  14. 11/11/10 14:42:49 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000000_0' done.  
  15. 11/11/10 14:42:49 INFO mapred.MapTask: io.sort.mb = 100  
  16. 11/11/10 14:42:50 INFO mapred.MapTask: data buffer = 79691776/99614720  
  17. 11/11/10 14:42:50 INFO mapred.MapTask: record buffer = 262144/327680  
  18. 11/11/10 14:42:50 INFO mapred.JobClient:  map 100% reduce 0%  
  19. 11/11/10 14:42:51 INFO mapred.MapTask: Starting flush of map output  
  20. 11/11/10 14:42:51 INFO mapred.MapTask: Finished spill 0  
  21. 11/11/10 14:42:51 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000001_0 is done. And is in the process of commiting  
  22. 11/11/10 14:42:51 INFO mapred.LocalJobRunner:   
  23. 11/11/10 14:42:51 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000001_0' done.  
  24. 11/11/10 14:42:51 INFO mapred.MapTask: io.sort.mb = 100  
  25. 11/11/10 14:42:51 INFO mapred.MapTask: data buffer = 79691776/99614720  
  26. 11/11/10 14:42:51 INFO mapred.MapTask: record buffer = 262144/327680  
  27. 11/11/10 14:42:53 INFO mapred.MapTask: Starting flush of map output  
  28. 11/11/10 14:42:53 INFO mapred.MapTask: Finished spill 0  
  29. 11/11/10 14:42:53 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000002_0 is done. And is in the process of commiting  
  30. 11/11/10 14:42:53 INFO mapred.LocalJobRunner:   
  31. 11/11/10 14:42:53 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000002_0' done.  
  32. 11/11/10 14:42:53 INFO mapred.MapTask: io.sort.mb = 100  
  33. 11/11/10 14:42:53 INFO mapred.MapTask: data buffer = 79691776/99614720  
  34. 11/11/10 14:42:53 INFO mapred.MapTask: record buffer = 262144/327680  
  35. 11/11/10 14:42:54 INFO mapred.MapTask: Starting flush of map output  
  36. 11/11/10 14:42:54 INFO mapred.MapTask: Finished spill 0  
  37. 11/11/10 14:42:54 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000003_0 is done. And is in the process of commiting  
  38. 11/11/10 14:42:54 INFO mapred.LocalJobRunner:   
  39. 11/11/10 14:42:54 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000003_0' done.  
  40. 11/11/10 14:42:54 INFO mapred.LocalJobRunner:   
  41. 11/11/10 14:42:54 INFO mapred.Merger: Merging 4 sorted segments  
  42. 11/11/10 14:42:54 INFO mapred.Merger: Down to the last merge-pass, with 4 segments left of total size: 2980 bytes  
  43. 11/11/10 14:42:54 INFO mapred.LocalJobRunner:   
  44. 11/11/10 14:42:55 INFO mapred.TaskRunner: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting  
  45. 11/11/10 14:42:55 INFO mapred.LocalJobRunner:   
  46. 11/11/10 14:42:55 INFO mapred.TaskRunner: Task attempt_local_0001_r_000000_0 is allowed to commit now  
  47. 11/11/10 14:42:55 INFO output.FileOutputCommitter: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://an.local:9100/user/an/out2  
  48. 11/11/10 14:42:55 INFO mapred.LocalJobRunner: reduce > reduce  
  49. 11/11/10 14:42:55 INFO mapred.TaskRunner: Task 'attempt_local_0001_r_000000_0' done.  
  50. 11/11/10 14:42:55 INFO mapred.JobClient:  map 100% reduce 100%  
  51. 11/11/10 14:42:55 INFO mapred.JobClient: Job complete: job_local_0001  
  52. 11/11/10 14:42:55 INFO mapred.JobClient: Counters: 14  
  53. 11/11/10 14:42:55 INFO mapred.JobClient:   FileSystemCounters  
  54. 11/11/10 14:42:55 INFO mapred.JobClient:     FILE_BYTES_READ=86081  
  55. 11/11/10 14:42:55 INFO mapred.JobClient:     HDFS_BYTES_READ=40373  
  56. 11/11/10 14:42:55 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=181846  
  57. 11/11/10 14:42:55 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=2642  
  58. 11/11/10 14:42:55 INFO mapred.JobClient:   Map-Reduce Framework  
  59. 11/11/10 14:42:55 INFO mapred.JobClient:     Reduce input groups=165  
  60. 11/11/10 14:42:55 INFO mapred.JobClient:     Combine output records=0  
  61. 11/11/10 14:42:55 INFO mapred.JobClient:     Map input records=165  
  62. 11/11/10 14:42:55 INFO mapred.JobClient:     Reduce shuffle bytes=0  
  63. 11/11/10 14:42:55 INFO mapred.JobClient:     Reduce output records=165  
  64. 11/11/10 14:42:55 INFO mapred.JobClient:     Spilled Records=330  
  65. 11/11/10 14:42:55 INFO mapred.JobClient:     Map output bytes=2642  
  66. 11/11/10 14:42:55 INFO mapred.JobClient:     Combine input records=0  
  67. 11/11/10 14:42:55 INFO mapred.JobClient:     Map output records=165  
  68. 11/11/10 14:42:55 INFO mapred.JobClient:     Reduce input records=165  

从中可以看出第二个日志文件里面有四段这样的代码:

[java] view plaincopy
  1. 11/11/10 14:42:42 INFO mapred.MapTask: io.sort.mb = 100  
  2. 11/11/10 14:42:43 INFO mapred.JobClient:  map 0% reduce 0%  
  3. 11/11/10 14:42:46 INFO mapred.MapTask: data buffer = 79691776/99614720  
  4. 11/11/10 14:42:46 INFO mapred.MapTask: record buffer = 262144/327680  
  5. 11/11/10 14:42:49 INFO mapred.MapTask: Starting flush of map output  
  6. 11/11/10 14:42:49 INFO mapred.MapTask: Finished spill 0  

说明是被分成了四个split,分片是成功了。

下一个问题:

使用多文件输入,中间处理之后输出文件是跟输入文件同名的,只是不在同一个文件夹下面。

输入文件与输出文件一一对应

0 0
原创粉丝点击