平时我们写MapReduce程序的时候,在设置输入格式的时候,总会调用形如job.setInputFormatClass(KeyValueTextInputFormat.class);来保证输入文件按照我们想要的格式被读取。所有的输入格式都继承于InputFormat,这是一个抽象类,其子类有专门用于读取普通文件的FileInputFormat,用来读取数据库的DBInputFormat等等。

不同的InputFormat都会按自己的实现来读取输入数据并产生输入分片,一个输入分片会被单独的map task作为数据源。下面我们先看看这些输入分片(inputSplit)是什么样的。

InputSplit:

我们知道Mappers的输入是一个一个的输入分片,称InputSplit。InputSplit是一个抽象类,它在逻辑上包含了提供给处理这个InputSplit的Mapper的所有K-V对。

  1. public abstract class InputSplit {
  2. public abstract long getLength() throws IOException, InterruptedException;
  3. public abstract
  4. String[] getLocations() throws IOException, InterruptedException;
  5. }

getLength()用来获取InputSplit的大小,以支持对InputSplits进行排序,而getLocations()则用来获取存储分片的位置列表。
  我们来看一个简单InputSplit子类:FileSplit。

  1. public class FileSplit extends InputSplit implements Writable {
  2. private Path file;
  3. private long start;
  4. private long length;
  5. private String[] hosts;
  6. FileSplit() {}
  7. public FileSplit(Path file, long start, long length, String[] hosts) {
  8. this.file = file;
  9. this.start = start;
  10. this.length = length;
  11. this.hosts = hosts;
  12. }
  13. //序列化、反序列化方法,获得hosts等等……
  14. }

从上面的源码我们可以看到,一个FileSplit是由文件路径,分片开始位置,分片大小和存储分片数据的hosts列表组成,由这些信息我们就可以从输入文件中切分出提供给单个Mapper的输入数据。这些属性会在Constructor设置,我们在后面会看到这会在InputFormat的getSplits()中构造这些分片。

我们再看CombineFileSplit:

  1. public class CombineFileSplit extends InputSplit implements Writable {
  2. private Path[] paths;
  3. private long[] startoffset;
  4. private long[] lengths;
  5. private String[] locations;
  6. private long totLength;
  7. public CombineFileSplit() {}
  8. public CombineFileSplit(Path[] files, long[] start,
  9. long[] lengths, String[] locations) {
  10. initSplit(files, start, lengths, locations);
  11. }
  12. public CombineFileSplit(Path[] files, long[] lengths) {
  13. long[] startoffset = new long[files.length];
  14. for (int i = 0; i < startoffset.length; i++) {
  15. startoffset[i] = 0;
  16. }
  17. String[] locations = new String[files.length];
  18. for (int i = 0; i < locations.length; i++) {
  19. locations[i] = "";
  20. }
  21. initSplit(files, startoffset, lengths, locations);
  22. }
  23. private void initSplit(Path[] files, long[] start,
  24. long[] lengths, String[] locations) {
  25. this.startoffset = start;
  26. this.lengths = lengths;
  27. this.paths = files;
  28. this.totLength = 0;
  29. this.locations = locations;
  30. for(long length : lengths) {
  31. totLength += length;
  32. }
  33. }
  34. //一些getter和setter方法,和序列化方法
  35. }

与FileSplit类似,CombineFileSplit同样包含文件路径,分片起始位置,分片大小和存储分片数据的host列表,由于CombineFileSplit是针对小文件的,它把很多小文件包在一个InputSplit内,这样一个Mapper就可以处理很多小文件。要知道我们上面的FileSplit是对应一个输入文件的,也就是说如果用FileSplit对应的FileInputFormat来作为输入格式,那么即使文件特别小,也是单独计算成一个输入分片来处理的。当我们的输入是由大量小文件组成的,就会导致有同样大量的InputSplit,从而需要同样大量的Mapper来处理,这将很慢,想想有一堆map task要运行!!这是不符合Hadoop的设计理念的,Hadoop是为处理大文件优化的。

最后介绍TagInputSplit,这个类就是封装了一个InputSplit,然后加了一些tags在里面满足我们需要这些tags数据的情况,我们从下面就可以一目了然。

  1. class TaggedInputSplit extends InputSplit implements Configurable, Writable {
  2. private Class<? extends InputSplit> inputSplitClass;
  3. private InputSplit inputSplit;
  4. @SuppressWarnings("unchecked")
  5. private Class<? extends InputFormat> inputFormatClass;
  6. @SuppressWarnings("unchecked")
  7. private Class<? extends Mapper> mapperClass;
  8. private Configuration conf;
  9. //getters and setters,序列化方法,getLocations()、getLength()等
  10. }

现在我们对InputSplit的概念有了一些了解,我们继续看它是怎么被使用和计算出来的。

InputFormat:

通过使用InputFormat,MapReduce框架可以做到:

1、验证作业的输入的正确性

2、将输入文件切分成逻辑的InputSplits,一个InputSplit将被分配给一个单独的Mapper task

3、提供RecordReader的实现,这个RecordReader会从InputSplit中正确读出一条一条的K-V对供Mapper使用。

  1. public abstract class InputFormat<K, V> {
  2. public abstract
  3. List<InputSplit> getSplits(JobContext context
  4. ) throws IOException, InterruptedException;
  5. public abstract
  6. RecordReader<K,V> createRecordReader(InputSplit split,
  7. TaskAttemptContext context
  8. ) throws IOException,
  9. InterruptedException;
  10. }

上面是InputFormat的源码,getSplits用来获取由输入文件计算出来的InputSplits,我们在后面会看到计算InputSplits的时候会考虑到输入文件是否可分割、文件存储时分块的大小和文件大小等因素;而createRecordReader()提供了前面第三点所说的RecordReader的实现,以将K-V对从InputSplit中正确读出来,比如LineRecordReader就以偏移值为key,一行的数据为value,这就使得所有其createRecordReader()返回了LineRecordReader的InputFormat都是以偏移值为key,一行数据为value的形式读取输入分片的。

FileInputFormat:

PathFilter被用来进行文件筛选,这样我们就可以控制哪些文件要作为输入,哪些不作为输入。PathFilter有一个accept(Path)方法,当接收的Path要被包含进来,就返回true,否则返回false。可以通过设置mapred.input.pathFilter.class来设置用户自定义的PathFilter。

  1. public interface PathFilter {
  2. boolean accept(Path path);
  3. }

FileInputFormat是InputFormat的子类,它包含了一个MultiPathFilter,这个MultiPathFilter由一个过滤隐藏文件(名字前缀为'-'或'.')的PathFilter和一些可能存在的用户自定义的PathFilters组成,MultiPathFilter会在listStatus()方法中使用,而listStatus()方法又被getSplits()方法用来获取输入文件,也就是说实现了在获取输入分片前先进行文件过滤。

  1. private static class MultiPathFilter implements PathFilter {
  2. private List<PathFilter> filters;
  3. public MultiPathFilter(List<PathFilter> filters) {
  4. this.filters = filters;
  5. }
  6. public boolean accept(Path path) {
  7. for (PathFilter filter : filters) {
  8. if (!filter.accept(path)) {
  9. return false;
  10. }
  11. }
  12. return true;
  13. }
  14. }

这些PathFilter会在listStatus()方法中用到,listStatus()是用来获取输入数据列表的。

下面是FileInputFormat的getSplits()方法,它首先得到分片的最小值minSize和最大值maxSize,它们会被用来计算分片大小。可以通过设置mapred.min.split.size和mapred.max.split.size来设置。splits链表用来存储计算得到的输入分片,files则存储作为由listStatus()获取的输入文件列表。然后对于每个输入文件,判断是否可以分割,通过computeSplitSize计算出分片大小splitSize,计算方法是:Math.max(minSize, Math.min(maxSize, blockSize));也就是保证在minSize和maxSize之间,且如果minSize<=blockSize<=maxSize,则设为blockSize。然后我们根据这个splitSize计算出每个文件的inputSplits集合,然后加入分片列表splits中。注意到我们生成InputSplit的时候按上面说的使用文件路径,分片起始位置,分片大小和存放这个文件的hosts列表来创建。最后我们还设置了输入文件数量:mapreduce.input.num.files。

  1. public List<InputSplit> getSplits(JobContext job
  2. ) throws IOException {
  3. long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
  4. long maxSize = getMaxSplitSize(job);
  5. // generate splits
  6. List<InputSplit> splits = new ArrayList<InputSplit>();
  7. List<FileStatus>files = listStatus(job);
  8. for (FileStatus file: files) {
  9. Path path = file.getPath();
  10. FileSystem fs = path.getFileSystem(job.getConfiguration());
  11. long length = file.getLen();
  12. BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);
  13. if ((length != 0) && isSplitable(job, path)) {
  14. long blockSize = file.getBlockSize();
  15. long splitSize = computeSplitSize(blockSize, minSize, maxSize);
  16. long bytesRemaining = length;
  17. while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
  18. int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
  19. splits.add(new FileSplit(path, length-bytesRemaining, splitSize,
  20. blkLocations[blkIndex].getHosts()));
  21. bytesRemaining -= splitSize;
  22. }
  23. if (bytesRemaining != 0) {
  24. splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,
  25. blkLocations[blkLocations.length-1].getHosts()));
  26. }
  27. } else if (length != 0) {
  28. splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts()));
  29. } else {
  30. //Create empty hosts array for zero length files
  31. splits.add(new FileSplit(path, 0, length, new String[0]));
  32. }
  33. }
  34. // Save the number of input files in the job-conf
  35. job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
  36. LOG.debug("Total # of splits: " + splits.size());
  37. return splits;
  38. }
  39. //……setters and getters

就这样,利用FileInputFormat 的getSplits方法,我们就计算出了我们的作业的所有输入分片了。

那这些计算出来的分片是怎么被map读取出来的呢?就是InputFormat中的另一个方法createRecordReader(),FileInputFormat并没有对这个方法做具体的要求,而是交给子类自行去实现它。
RecordReader
  RecordReader是用来从一个输入分片中读取一个一个的K -V 对的抽象类,我们可以将其看作是在InputSplit上的迭代器。我们从类图中可以看到它的一些方法,最主要的方法就是nextKeyvalue()方法,由它获取分片上的下一个K-V 对。

我们再深入看看上面提到的RecordReader的一个子类:LineRecordReader。

LineRecordReader由一个FileSplit构造出来,start是这个FileSplit的起始位置,pos是当前读取分片的位置,end是分片结束位置,in是打开的一个读取这个分片的输入流,它是使用这个FileSplit对应的文件名来打开的。key和value则分别是每次读取的K-V对。然后我们还看到可以利用getProgress()来跟踪读取分片的进度,这个函数就是根据已经读取的K-V对占总K-V对的比例来显示进度的。

  1. public class LineRecordReader extends RecordReader<LongWritable, Text> {
  2. private static final Log LOG = LogFactory.getLog(LineRecordReader.class);
  3. private CompressionCodecFactory compressionCodecs = null;
  4. private long start;
  5. private long pos;
  6. private long end;
  7. private LineReader in;
  8. private int maxLineLength;
  9. private LongWritable key = null;
  10. private Text value = null;
  11. //我们知道LineRecordReader是读取一个InputSplit的,它从InputSplit中不断以其定义的格式读取K-V对
  12. //initialize函数主要是计算分片的始末位置,以及打开想要的输入流以供读取K-V对,输入流另外处理分片经过压缩的情况
  13. public void initialize(InputSplit genericSplit,
  14. TaskAttemptContext context) throws IOException {
  15. FileSplit split = (FileSplit) genericSplit;
  16. Configuration job = context.getConfiguration();
  17. this.maxLineLength = job.getInt("mapred.linerecordreader.maxlength",
  18. Integer.MAX_VALUE);
  19. start = split.getStart();
  20. end = start + split.getLength();
  21. final Path file = split.getPath();
  22. compressionCodecs = new CompressionCodecFactory(job);
  23. final CompressionCodec codec = compressionCodecs.getCodec(file);
  24. // open the file and seek to the start of the split
  25. FileSystem fs = file.getFileSystem(job);
  26. FSDataInputStream fileIn = fs.open(split.getPath());
  27. boolean skipFirstLine = false;
  28. if (codec != null) {
  29. in = new LineReader(codec.createInputStream(fileIn), job);
  30. end = Long.MAX_VALUE;
  31. } else {
  32. if (start != 0) {
  33. skipFirstLine = true;
  34. --start;
  35. fileIn.seek(start);
  36. }
  37. in = new LineReader(fileIn, job);
  38. }
  39. if (skipFirstLine) {  // skip first line and re-establish "start".
  40. start += in.readLine(new Text(), 0,
  41. (int)Math.min((long)Integer.MAX_VALUE, end - start));
  42. }
  43. this.pos = start;
  44. }
  45. public boolean nextKeyValue() throws IOException {
  46. if (key == null) {
  47. key = new LongWritable();
  48. }
  49. key.set(pos); //对于LineRecordReader来说,它以偏移值为key,以一行为value
  50. if (value == null) {
  51. value = new Text();
  52. }
  53. int newSize = 0;
  54. while (pos < end) {
  55. newSize = in.readLine(value, maxLineLength,
  56. Math.max((int)Math.min(Integer.MAX_VALUE, end-pos),
  57. maxLineLength));
  58. if (newSize == 0) {
  59. break;
  60. }
  61. pos += newSize;
  62. if (newSize < maxLineLength) {
  63. break;
  64. }
  65. // line too long. try again
  66. LOG.info("Skipped line of size " + newSize + " at pos " +
  67. (pos - newSize));
  68. }
  69. if (newSize == 0) {
  70. key = null;
  71. value = null;
  72. return false;
  73. } else {
  74. return true;
  75. }
  76. }
  77. @Override
  78. public LongWritable getCurrentKey() {
  79. return key;
  80. }
  81. @Override
  82. public Text getCurrentValue() {
  83. return value;
  84. }
  85. /**
  86. * Get the progress within the split
  87. */
  88. public float getProgress() {
  89. if (start == end) {
  90. return 0.0f;
  91. } else {
  92. return Math.min(1.0f, (pos - start) / (float)(end - start));//读取进度由已读取InputSplit大小比总InputSplit大小
  93. }
  94. }
  95. public synchronized void close() throws IOException {
  96. if (in != null) {
  97. in.close();
  98. }
  99. }
  100. }

其它的一些RecordReader如SequenceFileRecordReader,CombineFileRecordReader.java等则对应不同的InputFormat。

下面继续看看这些RecordReader是如何被MapReduce框架使用的。

我们先看看Mapper.class是什么样的:

  1. public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
  2. public class Context
  3. extends MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  4. public Context(Configuration conf, TaskAttemptID taskid,
  5. RecordReader<KEYIN,VALUEIN> reader,
  6. RecordWriter<KEYOUT,VALUEOUT> writer,
  7. OutputCommitter committer,
  8. StatusReporter reporter,
  9. InputSplit split) throws IOException, InterruptedException {
  10. super(conf, taskid, reader, writer, committer, reporter, split);
  11. }
  12. }
  13. /**
  14. * Called once at the beginning of the task.
  15. */
  16. protected void setup(Context context
  17. ) throws IOException, InterruptedException {
  18. // NOTHING
  19. }
  20. /**
  21. * Called once for each key/value pair in the input split. Most applications
  22. * should override this, but the default is the identity function.
  23. */
  24. @SuppressWarnings("unchecked")
  25. protected void map(KEYIN key, VALUEIN value,
  26. Context context) throws IOException, InterruptedException {
  27. context.write((KEYOUT) key, (VALUEOUT) value);
  28. }
  29. /**
  30. * Called once at the end of the task.
  31. */
  32. protected void cleanup(Context context
  33. ) throws IOException, InterruptedException {
  34. // NOTHING
  35. }
  36. /**
  37. * Expert users can override this method for more complete control over the
  38. * execution of the Mapper.
  39. * @param context
  40. * @throws IOException
  41. */
  42. public void run(Context context) throws IOException, InterruptedException {
  43. setup(context);
  44. while (context.nextKeyValue()) {
  45. map(context.getCurrentKey(), context.getCurrentValue(), context);
  46. }
  47. cleanup(context);
  48. }

我们写MapReduce程序的时候,我们写的mapper都要继承这个Mapper.class,通常我们会重写map()方法,map()每次接受一个K-V对,然后我们对这个K-V对进行处理,再分发出处理后的数据。我们也可能重写setup()以对这个map task进行一些预处理,比如创建一个List之类的;我们也可能重写cleanup()方法对做一些处理后的工作,当然我们也可能在cleanup()中写出K-V对。举个例子就是:InputSplit的数据是一些整数,然后我们要在mapper中算出它们的和。我们就可以在先设置个sum属性,然后map()函数处理一个K-V对就是将其加到sum上,最后在cleanup()函数中调用context.write(key,value);

最后我们看看Mapper.class中的run()方法,它相当于map task的驱动,我们可以看到run()方法首先调用setup()进行初始操作,然后对每个context.nextKeyValue()获取的K-V对,就调用map()函数进行处理,最后调用cleanup()做最后的处理。事实上,从text他.nextKeyValue()就是使用了相应的RecordReader来获取K-V对的。

我们看看Mapper.class中的Context类,它继承与MapContext,使用了一个RecordReader进行构造。下面我们再看这个MapContext

  1. public class MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT>
  2. extends TaskInputOutputContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  3. private RecordReader<KEYIN,VALUEIN> reader;
  4. private InputSplit split;
  5. public MapContext(Configuration conf, TaskAttemptID taskid,
  6. RecordReader<KEYIN,VALUEIN> reader,
  7. RecordWriter<KEYOUT,VALUEOUT> writer,
  8. OutputCommitter committer,
  9. StatusReporter reporter,
  10. InputSplit split) {
  11. super(conf, taskid, writer, committer, reporter);
  12. this.reader = reader;
  13. this.split = split;
  14. }
  15. /**
  16. * Get the input split for this map.
  17. */
  18. public InputSplit getInputSplit() {
  19. return split;
  20. }
  21. @Override
  22. public KEYIN getCurrentKey() throws IOException, InterruptedException {
  23. return reader.getCurrentKey();
  24. }
  25. @Override
  26. public VALUEIN getCurrentValue() throws IOException, InterruptedException {
  27. return reader.getCurrentValue();
  28. }
  29. @Override
  30. public boolean nextKeyValue() throws IOException, InterruptedException {
  31. return reader.nextKeyValue();
  32. }
  33. }

我们可以看到MapContext直接是使用传入的RecordReader来进行K-V对的读取了。

到现在,我们已经知道输入文件是如何被读取、过滤、分片、读出K-V对,然后交给我们的Mapper类来处理的了。

最后,我们来看看FileInputFormat的几个子类。

TextInputFormat:

TextInputFormat是FileInputFormat的子类,其createRecordReader()方法返回的就是LineRecordReader。

  1. public class TextInputFormat extends FileInputFormat<LongWritable, Text> {
  2. @Override
  3. public RecordReader<LongWritable, Text>
  4. createRecordReader(InputSplit split,
  5. TaskAttemptContext context) {
  6. return new LineRecordReader();
  7. }
  8. @Override
  9. protected boolean isSplitable(JobContext context, Path file) {
  10. CompressionCodec codec =
  11. new CompressionCodecFactory(context.getConfiguration()).getCodec(file);
  12. return codec == null;
  13. }
  14. }

我们还看到isSplitable()方法,当文件使用压缩的形式,这个文件就不可分割,否则就读取不到正确的数据了。这从某种程度上将影响分片的计算。有时我们希望一个文件只被一个Mapper处理的时候,我们就可以重写isSplitable()方法,告诉MapReduce框架,我哪些文件可以分割,哪些文件不能分割而只能作为一个分片。

NLineInputFormat;

  NLineInputFormat也是FileInputFormat的子类,与名字一致,它是根据行数来划分InputSplits而不是像TextInputFormat那样依赖分片大小和行的长度的。也就是说,TextInputFormat当一行很长或分片比较小时,获取的分片可能只包含很少的K-V对,这样一个map task处理的K-V对就很少,这可能很不理想。因此我们可以使用NLineInputFormat来控制一个map task处理的K-V对,这是通过分割InputSplits时按行数分割的方法来实现的,这我们在代码中可以看出来。我们可以设置mapreduce.input.lineinputformat.linespermap来设置这个行数。
  1. public class NLineInputFormat extends FileInputFormat<LongWritable, Text> {
  2. public static final String LINES_PER_MAP =
  3. "mapreduce.input.lineinputformat.linespermap";
  4. public RecordReader<LongWritable, Text> createRecordReader(
  5. InputSplit genericSplit, TaskAttemptContext context)
  6. throws IOException {
  7. context.setStatus(genericSplit.toString());
  8. return new LineRecordReader();
  9. }
  10. /**
  11. * Logically splits the set of input files for the job, splits N lines
  12. * of the input as one split.
  13. *
  14. * @see FileInputFormat#getSplits(JobContext)
  15. */
  16. public List<InputSplit> getSplits(JobContext job)
  17. throws IOException {
  18. List<InputSplit> splits = new ArrayList<InputSplit>();
  19. int numLinesPerSplit = getNumLinesPerSplit(job);
  20. for (FileStatus status : listStatus(job)) {
  21. splits.addAll(getSplitsForFile(status,
  22. job.getConfiguration(), numLinesPerSplit));
  23. }
  24. return splits;
  25. }
  26. public static List<FileSplit> getSplitsForFile(FileStatus status,
  27. Configuration conf, int numLinesPerSplit) throws IOException {
  28. List<FileSplit> splits = new ArrayList<FileSplit> ();
  29. Path fileName = status.getPath();
  30. if (status.isDir()) {
  31. throw new IOException("Not a file: " + fileName);
  32. }
  33. FileSystem  fs = fileName.getFileSystem(conf);
  34. LineReader lr = null;
  35. try {
  36. FSDataInputStream in  = fs.open(fileName);
  37. lr = new LineReader(in, conf);
  38. Text line = new Text();
  39. int numLines = 0;
  40. long begin = 0;
  41. long length = 0;
  42. int num = -1;
  43. while ((num = lr.readLine(line)) > 0) {
  44. numLines++;
  45. length += num;
  46. if (numLines == numLinesPerSplit) {
  47. // NLineInputFormat uses LineRecordReader, which always reads
  48. // (and consumes) at least one character out of its upper split
  49. // boundary. So to make sure that each mapper gets N lines, we
  50. // move back the upper split limits of each split
  51. // by one character here.
  52. if (begin == 0) {
  53. splits.add(new FileSplit(fileName, begin, length - 1,
  54. new String[] {}));
  55. } else {
  56. splits.add(new FileSplit(fileName, begin - 1, length,
  57. new String[] {}));
  58. }
  59. begin += length;
  60. length = 0;
  61. numLines = 0;
  62. }
  63. }
  64. if (numLines != 0) {
  65. splits.add(new FileSplit(fileName, begin, length, new String[]{}));
  66. }
  67. } finally {
  68. if (lr != null) {
  69. lr.close();
  70. }
  71. }
  72. return splits;
  73. }
  74. /**
  75. * Set the number of lines per split
  76. * @param job the job to modify
  77. * @param numLines the number of lines per split
  78. */
  79. public static void setNumLinesPerSplit(Job job, int numLines) {
  80. job.getConfiguration().setInt(LINES_PER_MAP, numLines);
  81. }
  82. /**
  83. * Get the number of lines per split
  84. * @param job the job
  85. * @return the number of lines per split
  86. */
  87. public static int getNumLinesPerSplit(JobContext job) {
  88. return job.getConfiguration().getInt(LINES_PER_MAP, 1);
  89. }

现在,我们对Hadoop的输入格式和其在MapReduce中如何被使用有了具体的了解了。

 

(一)MapReduce篇之InputFormat,InputSplit,RecordReader(转)的更多相关文章

  1. [Hadoop源码解读](一)MapReduce篇之InputFormat

    平时我们写MapReduce程序的时候,在设置输入格式的时候,总会调用形如job.setInputFormatClass(KeyValueTextInputFormat.class);来保证输入文件按 ...

  2. [Hadoop源码解读](六)MapReduce篇之MapTask类

    MapTask类继承于Task类,它最主要的方法就是run(),用来执行这个Map任务. run()首先设置一个TaskReporter并启动,然后调用JobConf的getUseNewAPI()判断 ...

  3. [Hadoop源码解读](四)MapReduce篇之Counter相关类

    当我们定义一个Counter时,我们首先要定义一枚举类型: public static enum MY_COUNTER{ CORRUPTED_DATA_COUNTER, NORMAL_DATA_COU ...

  4. [Hadoop源码解读](二)MapReduce篇之Mapper类

    前面在讲InputFormat的时候,讲到了Mapper类是如何利用RecordReader来读取InputSplit中的K-V对的. 这一篇里,开始对Mapper.class的子类进行解读. 先回忆 ...

  5. MapReduce之自定义InputFormat

    在企业开发中,Hadoop框架自带的InputFormat类型不能满足所有应用场景,需要自定义InputFormat来解决实际问题. 自定义InputFormat步骤如下: (1)自定义一个类继承Fi ...

  6. [Hadoop源码解读](三)MapReduce篇之Job类

    下面,我们只涉及MapReduce 1,而不涉及YARN. 当我们在写MapReduce程序的时候,通常,在main函数里,我们会像下面这样做.建立一个Job对象,设置它的JobName,然后配置输入 ...

  7. Hadoop2源码分析-MapReduce篇

    1.概述 前面我们已经对Hadoop有了一个初步认识,接下来我们开始学习Hadoop的一些核心的功能,其中包含mapreduce,fs,hdfs,ipc,io,yarn,今天为大家分享的是mapred ...

  8. [Hadoop源码解读](五)MapReduce篇之Writable相关类

    前面讲了InputFormat,就顺便讲一下Writable的东西吧,本来应当是放在HDFS中的. 当要在进程间传递对象或持久化对象的时候,就需要序列化对象成字节流,反之当要将接收到或从磁盘读取的字节 ...

  9. MapReduce自定义InputFormat,RecordReader

    MapReduce默认的InputFormat是TextInputFormat,且key是偏移量,value是文本,自定义InputFormat需要实现FileInputFormat,并重写creat ...

随机推荐

  1. iOS 给UILabel文字加下划线

    摘自:http://blog.sina.com.cn/s/blog_6cd380c10101b6hn.html //带下划线的“注” NSMutableAttributedString可变的属性字符串 ...

  2. 花式玩转社交App,百变应用场景

    活动概况 时间:2016年03月20日13:30-16:30 地点:深圳腾讯大厦2楼多功能厅(南山区科技园科技中一路) 主办:APICloud.腾讯云分析和信鸽.云之讯 报名链接:http://act ...

  3. C3P0的详细配置说明

    C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSourc ...

  4. 做SSH练习的时候,JUunit运行遇到报错信息

    提示说是applicationcontext文件中的jdbcUrl信息错误,可是xml文件中的url再三确认是没有错的,不知道怎么回事? 最下面贴上applicationContext.xml中的数据 ...

  5. [BS] 小知识点总结-02

    1.  dispatch_GCD 可让某操作延迟x秒执行 //模拟网速慢,延迟3s返回数据(就会导致右侧数据和左侧标签不对应) dispatch_after(dispatch_time(DISPATC ...

  6. Android --设置Toast消失时间

    参考博客:Android开发,Toast重复显示(显示时间过长)解决方法 package com.dr.app.drseamoniter.toast; import android.content.C ...

  7. Java 静态类 static

    静态的方法是非虚方法(Java中的非虚方法有private,final,static,构造器,非虚方法无需根据具体的对象遍历方法区的方法表,决定调用关系) 也就是说,对于静态类型方法的调用,是其声明类 ...

  8. Sublime text 3 SVN插件及使用方法

    前提是你电脑里已装有SVN,这时只是设置快捷调用SVN. 1.通过快捷键 ctrl+` 或者 View > Show Console 菜单打开控制台 2.粘贴对应版本的代码后回车安装 适用于 S ...

  9. pptp建立vpn

    1. 安装依赖 ppp yum -y install ppp 2. 编译安装pptpd wget http://jaist.dl.sourceforge.net/project/poptop/pptp ...

  10. MVC3中 ViewBag、ViewData和TempData的使用和区别

    在MVC3开始,视图数据可以通过ViewBag属性访问,在MVC2中则是使用ViewData.MVC3中保留了ViewData的使用.ViewBag 是动态类型(dynamic),ViewData 是 ...