Hadoop自定义RecordReader

来源:互联网 发布:nginx非80端口配置 编辑:程序博客网 时间:2024/06/02 04:09

系统默认的LineRecordReader是按照每行的偏移量做为map输出时的key值,每行的内容作为map的value值,默认的分隔符是回车和换行。

现在要更改map对应的输入的<key,value>值,key对应的文件的路径(或者是文件名),value对应的是文件的内容(content)。

那么我们需要重写InputFormat和RecordReader,因为RecordReader是在InputFormat中调用的,当然重写RecordReader才是重点!

下面看代码InputFormat的重写:

[java] view plaincopy

  1. public class chDicInputFormat extends FileInputFormat<Text,Text> 
  2. implements JobConfigurable{ 
  3. private CompressionCodecFactory compressionCodecs = null; 
  4. public void configure(JobConf conf) { 
  5.         compressionCodecs = new CompressionCodecFactory(conf); 
  6.     } 
  7. protected boolean isSplitable(FileSystem fs, Path file) { 
  8. //  CompressionCodec codec = compressionCodecs.getCode(file);
  9. return false;//以文件为单位,每个单位作为一个split,即使单个文件的大小超过了64M,也就是Hadoop一个块得大小,也不进行分片
  10.     } 
  11. public RecordReader<Text,Text> getRecordReader(InputSplit genericSplit, 
  12.                             JobConf job, Reporter reporter) throws IOException{ 
  13.         reporter.setStatus(genericSplit.toString()); 
  14. return new chDicRecordReader(job,(FileSplit)genericSplit); 
  15.     } 

下面来看RecordReader的重写:

[java] view plaincopy

  1. public class chDicRecordReader implements RecordReader<Text,Text> { 
  2. private static final Log LOG = LogFactory.getLog(chDicRecordReader.class.getName()); 
  3. private CompressionCodecFactory compressionCodecs = null; 
  4. private long start; 
  5. private long pos; 
  6. private long end; 
  7. private byte[] buffer; 
  8. private String keyName; 
  9. private FSDataInputStream fileIn; 
  10. public chDicRecordReader(Configuration job,FileSplit split) throws IOException{ 
  11.         start = split.getStart(); //从中可以看出每个文件是作为一个split的
  12.         end = split.getLength() + start; 
  13. final Path path = split.getPath(); 
  14.         keyName = path.toString(); 
  15.         LOG.info("filename in hdfs is : " + keyName); 
  16. final FileSystem fs = path.getFileSystem(job); 
  17.         fileIn = fs.open(path); 
  18.         fileIn.seek(start); 
  19.         buffer = new byte[(int)(end - start)]; 
  20. this.pos = start; 
  21.     } 
  22. public Text createKey() { 
  23. return new Text(); 
  24.     } 
  25. public Text createValue() { 
  26. return new Text(); 
  27.     } 
  28. public long getPos() throws IOException{ 
  29. return pos; 
  30.     } 
  31. public float getProgress() { 
  32. if (start == end) { 
  33. return 0.0f; 
  34.         } else { 
  35. return Math.min(1.0f, (pos - start) / (float)(end - start)); 
  36.         } 
  37.     } 
  38. public boolean next(Text key, Text value) throws IOException{ 
  39. while(pos < end) { 
  40.             key.set(keyName); 
  41.             value.clear(); 
  42.             fileIn.readFully(pos,buffer); 
  43.             value.set(buffer); 
  44. //      LOG.info("---内容: " + value.toString());
  45.             pos += buffer.length; 
  46.             LOG.info("end is : " + end  + " pos is : " + pos); 
  47. return true; 
  48.         } 
  49. return false; 
  50.     } 
  51. public void close() throws IOException{ 
  52. if(fileIn != null) { 
  53.             fileIn.close(); 
  54.         } 
  55.     } 

通过上面的代码,然后再在main函数中设置InputFormat对应的类,就可以使用这种新的读入格式了。

对于那些需要对整个文档进行处理的工作来说,还是比较有效的。

OK,下一次需要将输入文件进行split。

需求是这样的,一个文本中存放的是一些文件的路径和名称,每一行代表一个文件

如下所示:

/mkbootimg/mkbootimg.c
/logwrapper/logwrapper.c
/adb/log_service.c
/adb/adb_client.c
/adb/usb_windows.c
/adb/get_my_path_darwin.c
/adb/usb_osx.c
/adb/file_sync_service.c
/adb/file_sync_client.c
/adb/usb_linux.c
/adb/fdevent.c
/adb/usb_linux_client.c
/adb/commandline.c
/adb/remount_service.c
/adb/sockets.c

要对这些文件分别进行处理,但是整个文本中包含数十万个这种的文本,想对这些文本在处理的时候进行分布式处理。

大家有什么好的建议可以提出来:

现在的一个思路是控制分块的大小,但是这个也不是太好,想用更好的方式,例如每10000行作为一个split,这样可以通过hadoop平台实现分布式,正在做.....求指点啊!