1.在Hadoop的jar包中的mapred包中找到MapTask和ReduceTask两个类
在Mapper类中的context对象包含了map的输入和输出
在MapTask中是没有主方法的,它是通过容器container反射成为一个对象,它来执行run方法,如下所示:
如果Reduce数量设定,则map端执行排序,没有设定reduce数量,则map端不排序
//在主函数WordCount类中设置Reducer任务数
job.setNumReduceTasks(2);
MapTask的run方法:
查看runNewMapper方法:
查看runNewMapper方法:
宏观上MapTask的主要任务:
1.输入初始化
2.调用Mapper类的run方法,也就是在这里实现了MapTask与用户的交互,框架最终调用的是Mapper的run方法。
3.读取完毕关闭input对象,并置为null,防止本次数据写到下次调用中
4.输出完毕关闭output对象,并置为null,防止本次数据写到下次调用中
MapTask运行起来之后,MapTask会去HDFS集群中,把Jar包,配置信息,切片信息拉取到本地的,
所以这里的jb信息和客户端书写的个性化配置信息是相同的。
这里taskContext.getMapperClass(),隐性的就是调用job.getMapperClass(),也就是配置信息去取mapClass
根据用户在配置文件中指定的类,反射我们手写的具体map类
定义input,用于从HDFS中读取切片信息,在定义的记录读取器中,传入了创建的split对象和输入格式化对象
进入NewTrackinngRecordReader对象,我们传入的输入格式化对象,创建了RecordReader对象,并赋值给real
客户端输入格式化inputFormat计算切片,map阶段,输入格式化(inputFormat)会给我们的输入对象创建一个记录读取器(RecordReader)。
在map中inputFormat创建了一个行读取器LineRecordReader来读取行数
由此可知,框架默认按行读取
进入LineRecordReader方法:
这里有Mapper类中的各种方法,也就是最终是LineRecordReader去执行读取任务
Mapper类:
至此构造完了Input对象
构建Map自己的上下文对象
MapContext上下文{包含nextKeyValue,getCurrentKey,getcurrentValue}
调用NewTrackingRecordReader{包含nextKey,ValuegetCurrentKey,getcurrentValue}
调用LineRecordReader{包含nextKeyValue,getCurrentKey,getcurrentValue}
在Map自己的上下文对象中,调用的是NewTrackingRecordReader中的方法
进入MapContextImpl的实现,它包含了Mapper类中要使用的三个方法,Mapper类调用NewTrackingRecordReader:
NewTrackingRecordReader调用LineRecordReader中的方法
上述构造完成之后进行各项初始化操作,input就是NewTrackingRecordReader对象,它依旧会去调用LineRecordReader对象,执行初始化。
由LineRecordReader对象real执行初始化:
来看LineRecordReader具体业务逻辑实现:
从切片中获取起始偏移量,起始偏移量加上切片长度就是结束偏移量,根据切片获取文件路径
根据配置文件job获取文件系统对象,然后打开文件进行读取,这个流open是FSDataInputStream输入流
因为是分布式文件系统,多个map一起读取数据,传入‘start’每个map根据自己的起始位置去读取数据
重点 :除了第一个切片,后续的所有切片都不读取第一行数据
至此map端完成初始化工作。
map在输入初始化过程中做的事情:
map的输入来自于hdfs,所以它准备了一个对hdfs一个切片对应的流,开启一个输入流,然后将偏移量seek到自己的切片位置,这时候这个流就可以读取自己的切片位置了,但是它先把各自的第一行读一下(所以New Text()是匿名的),然后算出字节数,加到自己的切片的起始位置上,这样就向下移动了一行,为的是在初始化准备的时候,将切片的偏移量向下移动一行,因为hdfs做块的切割的时候,有可能将数据切割成上下两个块的尾和头,那么就需要将下一个块的“头”,还给上一个块来处理。如果前后两个block在同一台机器上,那么直接本地读取,否则跨网拉取数据。规避数据不完整的问题。
处理过程:nextKeyValue
只要Mapper端的run方法调用起来,此处对key和value进行赋值操作
map端在input过程中主要做了两件事情,初始化(I/O流读取过程中:seek偏移量、切片第一行的让出)和nextKeyValue(对key,value赋值与更新)