使用hadoop jar执行mapreduce任务时首先从hdfs中读取数据将这些数据解析为inputsplit,然后再将inputsplit中的内容解析为一个一个的<k,v>键值对,这个过程就是有InputFormat的子类完成的。之前在MR例子中有一段代码job.setInputFormatClass(TextInputFormat.class);就是指定TextInputFormat来完成这项工作,这个类是hadoop默认的其实可以不写。
InputFormat是一个抽象类,类中有两个抽象方法List<InputSplit> getSplits和RecordReader<K,V>createRecordReader,getSplit负责将hdfs数据解析为InputSplit,createRecordReader负责将每个InputSplit中的每一行解析为<k,v>键值对。
TextInputFormat
getSplits
FileInputFormat继承了InputFormat并实现了getSplits方法。
主要完成的功能是:
根据路径解析hdfs数据,判断文件是否可以被切分。
计算splitSize,默认等于blockSize,128M
获取每一个hdfs对象并进行遍历并将结果放入List<InputSplit>中返回。
Hadoop中一个block对应一个inputsplit,一个inputsplit对应一个map任务。
注意:
hadoop不会对小于128M的文件进行切分,例如一个文件1G那就是8个map任务,如果有1000个100kb的文件则对应1000个map任务,这样会造成效率下降。所以MapReduce不适合处理小文件。
如果inputsplit和blocksize不一样比如大于,那么在解析为inputsplit时一个block就不够用,此时框架就会去别的节点上读取数据来构造inputsplit,这样会产生网络消耗影响效率。
createRecordReader
TextInputFormat继承了FileInputFormat并实现了createRecordReader方法。此方法的返回值是抽象类RecordReader,而最终返回的是LineRecordReader,LineRecordReader实现了RecordReader并在实现的抽象方法中完成解析。
主要完成的功能是:
在initialize方法中获取FileSplit对象并读取每一行内容。
获取<k,v>键值对作为map任务的入参再调用map任务。
框架每获取一个<k,v>就会调用一次map任务。
至此我们可以通过下图大概了解一下这几个类的关系
NlineInputFormat
Hadoop中默认是一个block一个inputsplit,但是在代码中可以指定其他的inputFormat