FileInputFormat(org.apache.hadoop.mapreduce.lib.input.FileInputFormat)是专门针对文件类型的数据源而设计的,也是一个抽象类,它提供两方面的作用:
 
(1)定义Job输入文件的静态方法;
(2)为输入文件形成切片的通用实现;
 
至于如何将切片中的数据转换为一条条的“记录”则根据文件类型的不同交由具体的子类负责实现。
 
FileInputFormat input paths
 
FileInputFormat提供了四个静态方法用于定义Job的输入文件路径:
 
public static void addInputPath(Job job, Path path)
public static void addInputPaths(Job job, String commaSeparatedPaths)
public static void setInputPaths(Job job, Path... inputPaths)
public static void setInputPaths(Job job, String commaSeparatedPaths)
 
addInputPath()、addInputPaths()用于“添加”一个(批)输入路径,可以重复被调用,源码如下:
 
 
addInputPath的每一次调用都会将输入路径(Path会被转换为字符串形式)与原有值以“,”分隔进行拼接(并不会覆盖原有值),并保存至Job Configuration的属性INPUT_DIR(mapreduce.input.fileinputformat.inputdir)中。
 
 
而addInputPaths实际是对addInputPath的循环调用。
 
setInputPaths()实际是两个重载方法,用于“设置”一个(批)输入路径,该方法用于一次性调用,每一次调用都会覆盖之前的结果,源码如下:
 
 
该方法的最后会替换Job Configuration属性INPUT_DIR(mapreduce.input.fileinputformat.inputdir)的原有值。
 
 
 
这里所说的输入路径可以代表一个文件,也可以代表一个目录(该目录下的所有文件将全部作为输入数据),而且可以在输入路径中使用通配符或者使用“,”进行多个输入路径的拼接。
 
注意:目录中的内容(子目录)不会被递归处理。实际上目录中应仅包含文件,如果目录中包含子目录,这些子目录会被当作文件处理,从而引发异常。如果我们不需要递归目录,我们可以通过File Pattern或者Filter(见后)告知FileInputFormat仅仅选取指定目录中的文件;如果我们确实需要递归处理目录,则可以通过设置mapreduce.input.fileinputformat.input.dir.recursive为true实现。
 
有些时候我们还需要“过滤”输入路径中的一些文件,这可以通过方法setInputPathFilter()为FileInputFormat设置相应的过滤器实现,源码如下:
 
 
实际就是指定一个PathFilter(PathFilter的相关内容不再讨论范围)的具体实现类名称,保存于Job Configuration属性PATHFILTER_CLASS(mapreduce.input.pathFilter.class)中。
 
如果我们没有显示设置PathFilter,FileInputFormat会有一个默认的过滤器,用于过滤目录中的隐藏文件;如果我们显示设置PathFilter,则FileInputFormat的过滤器实则是一个过滤器链,而默认的过滤器会居于过滤器链的首部,优先被执行。
 
综上所述,FileInputFormat的输入路径和过滤器实际可以直接通过相应的属性值进行设置,如下图所求:
 
 
FileInputFormat input splits
 
FileInputFormat生成切片的过程是由getSplits()方法实现的,核心逻辑及相关源码如下:
 
1. 确定切片大小的最小值与最大值;
 
 
最小值:getFormatMinSplitSize()与getMinSplitSize()两者之间的较大值。getFormatMinSplitSize()是FileInputFormat中的一个实例方法,默认返回值为1,即1字节;getMinSplitSize()返回值由属性mapreduce.input.fileinputformat.split.minsize决定,默认值为1,即1字节。如果没有特殊需要,最小值即为1字节。有些数据格式的文件对切片的最小大小是有要求的,如SequenceFile(具体可参考SequenceFile相关文档),这时就需要在FileInputFormat子类中重写getFormatMinSplitSize()方法来满足特定需求。
 
最大值:getMaxSplitSize()返回值由属性mapreduce.input.fileinputformat.split.maxsize决定,默认值为Long.MAX_VALUE。
 
2. 获取输入路径中的所有文件信息;
 
 
3. 迭代处理输入路径中的每一个文件,为每一个文件生成切片;
 
 
 
对于每一个文件而言,生成切片的过程大致可以概括为以下5个关键步骤:
 
获取文件的路径及长度(1);
如果文件长度为0,则生成一个“空”的切片(5);如果文件长度不为0,则获取文件的数据块信息(2);
如果文件格式不可切片,则将整个文件生成一个切片(4);如果文件格式可切片,则为该文件生成切片(3)。
 
其中文件格式是否支持切片,由FileInputFormat isSplitable()方法决定,默认返回值为true,即默认可切片。可以根据实际应用场景的不同,在FileInputFormat的子类中重写该方法,使返回值为false,达到禁止切片的功效,这样每一个Map Task会处理一个文件的全部数据。
 
在详细介绍第3步之前,需要先引入一个新的类FileSplit,它表示一个文件切片,包含的变量如下:
 
 
file:切片所引用的文件路径(名称);
start:切片在文件中的起始偏移量;
length:切片大小;
hosts:由切片在文件中的起始偏移量、切片大小、文件数据块信息可以计算出切片所引用的数据块有哪些(切片大小可能大于HDFS的数据块大小),hosts中保存着这些数据块中的第一个数据块的副本位置(主机名),默认为3个主机名,MapReduce根据此值完成Map Task的调度;
hostInfos:相对于hosts中保存着的主机名,还保存着副本是否位于主机内存的信息。
 
 
下面介绍一个(可切片)文件切片的形成过程,大体也可以分为5个步骤:
 
step1
 
首先获取文件的数据块大小blockSize(这里也可以看出不同的文件,数据块大小也是可以不同的);
 
然后根据数据块大小(blockSize)、切片最小值(minSize)、切片最大值(maxSize)计算文件对应的切片大小(splitSize),计算公式如下:
 
splitSize = max(minimumSize, min(maximumSize, blockSize))
 
step2
 
判断文件的剩余大小(未切片的大小)是否满足继续进行切片的条件:((double) bytesRemaining)/splitSize > SPLIT_SLOP为true,其中bytesRemaining初始值为文件长度length,SPLIT_SLOP值为1.1,且不可修改,即文件剩余大小需为切片大小(splitSize)的1.1倍才会继续切片。
 
step3
 
获取切片对应的数据块。一个切片根据切片大小的不同,可能会包含若干个数据块,这里将第一个数据块的副本位置作为切片的存储位置。
 
切片在文件中的起始偏移量的计算公式:
 
offset = (n - 1)* splitSize,n表示第几个切片
 
对于给定的切片的offset,getBlockIndex实际就是计算文件的哪个数据块的起止范围恰好包含offset,返回这个数据块在数据块列表(blkLocations)的下标,计算流程如下:
 
 
step4
 
根据下标对应的数据块信息构建一个FileSplit。根据FileSplit的信息,可以看出FileSplit并不实现保存数据,仅仅是通过文件名称、起始偏移量、大小关联数据,并将对应数据块的副本位置作为切片的存储位置进行Map Task的调度。
 
循环执行step2、step3、step4直到文件剩余大小无法满足切片条件。
 
step5
 
将文件的剩余部分构建一个FileSplit。
 
 
 

Hadoop FileInputFormat实现原理及源码分析的更多相关文章

  1. Hadoop CombineFileInputFormat实现原理及源码分析

    Hadoop适用于少量的大文件场景,而不是大量的小文件场景(这里的小文件通常指文件大小显著小于HDFS Block Size的文件),其主要原因是因为FileInputFormat在为这些小文件生成切 ...

  2. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  3. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...

  4. HashMap和ConcurrentHashMap实现原理及源码分析

    HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...

  5. (转)ReentrantLock实现原理及源码分析

    背景:ReetrantLock底层是基于AQS实现的(CAS+CHL),有公平和非公平两种区别. 这种底层机制,很有必要通过跟踪源码来进行分析. 参考 ReentrantLock实现原理及源码分析 源 ...

  6. 【转】HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  7. 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造

    原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论   自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...

  8. 《深入探索Netty原理及源码分析》文集小结

    <深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de

  9. HashMap实现原理及源码分析之JDK8

    继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap  基于JDK8的HashMap源码解析  [jdk1.8]HashMap源码分析 一.H ...

随机推荐

  1. 在Qt中使用sleep

      关于sleep函数,我们先来看一下他的作用:sleep函数是使调用sleep函数的线程休眠,线程主动放弃时间片.当经过指定的时间间隔后,再启动线程,继续执行代码.sleep函数并不能起到定时的作用 ...

  2. 案例:我行我素购物系统 v1.1

    系统逻辑结构: import java.util.Scanner; public class ShoppingSystem { public static void main(String[] arg ...

  3. codevs 1222 信与信封问题

    /* 二分图 题目给出的是确定不连通的边 如果我们拿剩下的可能联通也可能不连通的边跑最大匹配 如果不是完美非配 也就是说把所有可能的边都认为是一定的 这样都跑不出来(不能匹配到每个点)那么一定不能确定 ...

  4. 使用out来返回多个值

    一般的方法中,一次只能有一个返回值.但是当我们需要一个方法给我们返回多个值得时候应该怎么做呢?这时候可以使用out来修饰参数. out介绍: static void Main(string[] arg ...

  5. Servlet学习--练习示例总结

    醉了醉了..本来想测试下Servlet生命周期的,然后调了好久的错误,还是没成功,不知道为什么不能这样做 贴上代码: import java.io.IOException; import java.i ...

  6. 兼容所有浏览器的JQuery zClip插件实现复制到剪贴板功能

    相信这个功能大家平时上网经常能碰到,以前也没怎么留意怎么实现的,直到项目中需要. 网上一搜一大堆,单纯使用js方法也不是没有,但是由于各浏览器的安全机制不同,不是跨浏览器的.去看了几个常用的网站,都是 ...

  7. readonly 与 const

    readonly MSDN定义:readonly 关键字是可以在字段上使用的修饰符.当字段声明包括 readonly 修饰符时,该声明引入的字段赋值只能作为声明的一部分出现,或者出现在同一类的构造函数 ...

  8. 用Global.asax实现伪静态.

    在Global.asax文件里添加Application_BeginRequest事件处理.添加如下代码: 1 protected void Application_BeginRequest(Obje ...

  9. GET or POST

    w3school中是这么说的: 与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用. 然而,在以下情况中,请使用 POST 请求: 无法使用缓存文件(更新服务器上的文件或数据库) 向服 ...

  10. http请求头响应头大全

    转:http://www.jb51.net/article/51951.htm 本文为多篇“HTTP请求头相关文章”及<HTTP权威指南>一书的阅读后个人汇总整理版,以便于理解. 通常HT ...