1、Map输出跟踪:
以年份温度的mr程序为示例,进行分析: 【MyMapper.class】context.write(yearT, temperature);//将数据写出
-->【WrappedMapper.class】write(KEYOUT key, VALUEOUT value)
-->【TaskInputOutputContextImpl.class】write(KEYOUT key, VALUEOUT value)
-->【NewOutputCollector.class】write(K key, V value)
说明:构建NewOutputCollector对象时,调用构造器
//给属性MapOutputCollector赋值MapOutputBuffer对象
collector = createSortingCollector(job, reporter);
//获取分区数
partitions = jobContext.getNumReduceTasks();
如果分区数>1:
分区器,通过反射机制获取。参考如下:
ReflectionUtils.newInstance(jobContext.getPartitionerClass(), job)
【conf.getClass(PARTITIONER_CLASS_ATTR, HashPartitioner.class)】
获取默认的HashPartitioner分区器
用户自定义的话,会从job身上获取自定义的分区器
如果分区数是1:(默认情况)
不会采用MR提供的默认的分区器(如hashPartitioner),
而是使用匿名内部类
创建一个分区器,返回索引0.
如果分区数是0:
不走shuffle,涉及不到分区情况。
-->【MapOutputBuffer.class】collect(K key, V value, final int partition)
说明:收集器调用collect方法,将kv对存储在环形缓冲区中
在收集map输出数据时,收集完之后,有可能做了多次溢写,
还有可能收集到的所有数据不满足80M.
那么,不管什么情况,最后都会调用flush方法。
2、sortAndSpill()方法:
说明:当满足kvbuffer里的阈值,或者是最后一次flush操作,都会调用 sortAndSpill() 操作
//提前统计这次溢写是多少个字节长度:包含kv原始数据和分区的头信息
final long size = distanceTo(bufstart, bufend, bufvoid) +partitions * APPROX_HEADER_LENGTH;
//为这次溢写,创建一个溢写记录对象
final SpillRecord spillRec = new SpillRecord(partitions);
//为这次溢写,构造溢写文件路径,
final Path filename =mapOutputFile.getSpillFileForWrite(numSpills, size);
//调用文件系统对象的create方法,创建溢写文件&