FileInputFormat切片源码解析
相关视频
https://www.bilibili.com/video/BV1Qp4y1n7EN?p=89&vd_source=de9cfd633cf5a71794dc91a35f34cd48
MapTask切片
Job的提交流程去看前面的内容,这里只是说一下Job提交流程中的 MapTask切片
接着来看源代码,从自己写的Driver类,一层一层点进去。waitForCompletion
方法 -> submit
方法 -> submitJobInternal
方法 -> writeSplits
方法
writeSplits
方法中,核心部分就是被if-else语句包起来的两句代码
if语句中的表达式中的 getUseNewMapper
方法,用来判断是否使用了新API,返回值是布尔类型,如果配置中配置了 mapred.mapper.new-api
,就返回对应的配置;没配置的话,就返回false
然后根据使用的是新API还是旧API来选择切片方式,这里毫无疑问的是使用了新API的切片方式
点进 writeNewSplits
方法,这里的核心代码是 InputFormat
抽象类对象的 getSplits
方法;JobSplitWriter
类的 createSplitFiles
方法
InputFormat
抽象类的对象,是通过hadoop官方写的反射工具类获取的。然后作为参数,传入 getSplits
方法
这里使用的 InputFormat
抽象类的子类是 FileInputFormat
,进入到 FileInputFormat
类中,可以看到该类也是抽象类,我们这里使用的类实际上是 TextInputFormat
类,可通过debug观察到
FileInputFormat
类的子类,可以通过Idea工具,在类的页面中按下 Ctrl + H
查看,如下
也可以在 InputFormat
类中观察它的所有子类,如下
之后我们还会提到图中的其他几个类
然后言归正传,来解读一下 FileInputFormat
类中的 getSplits
方法
这个方法一上来就创建了两个变量。第一个变量 minSize
是通过比较两个参数的值,然后获取更大的那个值。两个参数中,第一个的值就是写死的 1;第二个值是获取配置文件中的 mapreduce.input.fileinputformat.split.minsize
,没有这个配置则返回 1。也就是说,minSize
这个变量最少就是 1
第二个变量 maxSize
的值的获取和 minSize
的第二个参数一样,获取配置文件中的信息,不同的地方在于,没有对应配置的话则取 2^63-1
接着往下看,看到for循环语句,这里遍历的是job上传的每一个文件,从这里也可以看出来,切片是针对每一个文件单独切片,而不是统一切片的
往下走,可以看到获取了文件长度,并判断了这个长度是否为0。实际意思就是获取了文件内容的字符长度,并根据字符长度判断是否是空文件。
再往下看,通过if语句校验了当前遍历的这个文件对象(FileStatus
)是否是本地文件类型(LocatedFileStatus
),然后做出相应的操作
再往下看,就是咱们的核心代码,isSplitable
方法,顾名思义。判断当前文件是否可以切割。
注意
例如只输入了一个文件,这个文件的大小是1T,正常来说是可以按照之前说过的128M为一块进行切割的
但是之后会讲到 “压缩”,压缩后的文件很有可能是不支持切割的,也就是说这一整个1T的文件,就只能让一个MapTask来处理
接着往下看,可以看到获取了块大小和切片大小的值,但是这里需要注意,咱们使用的是本地模式,本地模式会认为咱们的集群资源不够充足,所以这里的值是33554432,也就是32MB。切片大小,也是根据32MB的块大小来进行计算的
再往下走就是while循环,这里是非常精彩的一段代码:以咱们的案例来说,很显然不会执行这段代码,但是可以分析一下它的逻辑。
它判断的是文件长度除以块大小的比例是否大于1.1。如果当前文件长度是32.1MB,我们对额外的0.1MB进行数据切片,造成的结果就是第一片忙的要死,第二片闲的要死。
所以,他这里判断的逻辑是,数据长度除以切片大小,如果结果小于1.1,就不切了。否则就一直切,切完会在数据长度中减去切片大小继续进行切片,直到比例小于1.1为止。
while循环执行完后,划分的是最后剩下的一小个数据切片。可以看出,这段代码中,调用的 makeSplit
方法的第三个参数不同,上面写的是 splitSize
,也就是默认切片大小;而这里写的是 bytesRemaining
,也就是最后被切完的数据长度大小。获取到最后的切片大小之后,进行切片,执行到这里,切片就划分完毕了。
下面就没什么好说的了,退出 getSplits
方法。接着往下看,下面比较重要的代码是 JobSplitWriter
类中名为 createSplitFiles
的静态方法
在执行该方法之前,也就是执行完 getSplits
方法之后,并没有在之前说的job文件夹下创建切片文件,而是在 createSplitFiles
执行完创建的