hadoop源码解析一步到位

YarnChild,由NodeManager通过脚本启动
YarnChild的main方法中调用Task的run方法
task可以是maptask也可以是reducetask

163 taskFinal.run(job, umbilical); // run the task

在MapTask中,run方法调用了它自己的runNewMapper方法

784行MapTask调用了mapper的run方法

该run方法在哪里?

在MapTask的runNewMapper中,
创建了任务上下文对象
创建了Mapper对象,这个mapper对象是我们自己设置的
创建了FileInputFormat对象,默认是TextInputFormat.class的对象
创建了切片对象,用于读取输入的内容
创建了读取切片内容的输入流对象:NewTrackingRecordReader
创建了输出对象
reduce任务可以没有 runNewMapper 763行
创建Map任务的上下文件对象

开始调用map方法进行map任务的计算

重写的map方法中的context参数的几个方法
context
context.nextKeyValue()
mapContext.nextKeyValue();
context.getCurrentKey()
mapContext.getCurrentKey();
context.getCurrentValue()
mapContext.getCurrentValue();
context.write(key, value)
mapContext.write(key, value);

为什么不直接调用mapContext的方法而要转一下?
MapContext mapContext
Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context

因为Mapper中run方法要求必须是Context类型的,而Mapper中Context
是一个抽象类,可以将MapContext的实现跟Mapper中的Context进行解耦

此时四个方法如何实现,只需要看MapContext中是如何实现的。

KeyValueLineRecordReader

"hello bjsxt\tI'm OK"

key="hello bjsxt"
value="I'm OK"

如果有多个制表符,则以第一个为准

<hello, 4>
<bjsxt, 3>

hello\t4
bjsxt\t3

LineRecordReader(默认的方式?)

以行的偏移量为key,行的字符串为value的读取方式


MapTask中755行,input输入流是NewTrackingRecordReader对象

NewTrackingRecordReader是如何读取的?

mapper.run(mapperContext);中用到nextKeyValue()方法
这个方法是mapperContext的,mapperContext代理了mapContext的
mapContext代理了input的,input是NewTrackingRecordReader对象
所以要看NewTrackingRecordReader对象中的nextKeyValue方法

MapTask 512行:real是真正读取的
748行说明了默认的InputFormat是哪个实现类
JobContextImpl类中175行说明了默认情况下使用TextInputFormat作为
inputFormat的实现类

LineRecordReader
	该类中116行,判断,如果当前map处理的切片不是第一个切片
	则永远丢掉第一行
	该类180行说明,只要不是最后一个切片,则永远多读一行
	
	将block块有可能切开的一行拼成整体。
	
	
结论:map读取数据的时候,使用LineRecordReader来读取
	在nextKeyValue方法中读取

第四个方法:
context.write方法要找MapContextImpl的父类
TaskInputOutputContextImpl
该类87行:write方法
该write方法用的是output的write方法,该output是MapContextImpl
构造器中实例化父类的时候传过去的参数

在MapContextImpl构造器中的该参数是谁?
在实例化MapContextImpl的时候传的参数是哪个

MapTask类767行实例化出来的NewOutputCollector
NewOutputCollector类中的write方法:
711行:
  collector.collect(key, value,
                    partitioner.getPartition(key, value, partitions));
NewOutputCollector类中697行
如果分区数是1,则直接返回0分区
否则使用默认分区器计算分区:
	默认分区器是哪个?
	在JobContextImpl的235行,如果用户设置,则使用用户设置的
	否则使用HashPartitioner
	
	默认如何计算分区?
	(key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
	
NewOutputCollector类用于写map的输出数据到环形缓冲区
这个类通过collector的对象输出数据
collector是谁?在NewOutputCollector类695行进行初始化

MapTask第382行的方法用于创建collector对象,默认情况下,这个对象
是MapOutputBuffer类的对象 387行

MapOutputBuffer类的collect方法:

MapTask 965行  环形缓冲区默认阈值0.8   80%,
	当环形缓冲区写到80%后,开始向磁盘溢写数据
MapTask 967行,sortmb指的是环形缓冲区大小,默认100MB
981行将MB单位换算为Byte单位,在983行实例化出了缓存,byte数组。

MapOutputBuffer类的collect方法:
1065行开始执行collect方法,将键值对序列化好之后写到缓存中

在MapOutputBuffer的1255行compare方法先按照分区比较,再按照
key比较,要排序就要比较大小

看comparator如何实现

在1001行有赋值:


比较器:要么key的类型系统提供并注册到WritableComparator类的map
集合中,要么自己定义,自己注册

Text.class 本身提供了比较器,并且注册到WritableComparator类map
集合中了

Writable接口
WritableComparable接口


WritableComparator类中65行
如果没有注册过比较器,比如像自定义的key类型
WritableComparator会直接创建一个WritableComparator对象,其中
用到了自定义的key的类型
要求自定义的key类型实现WritableComparable<T>接口
自定义key类型的时候需要实现compareTo方法,在该方法中自定义比较
规则。

排序是如何排序的?
MapOutputBuffer的978行,默认使用QuickSort快排

1593行进行排序

进行快排的时候,调用了MapOutputBuffer的compare方法
compare方法用到了key类型注册的比较器,或者自定义的比较器

还可以继承WritableComparator类,重写compare方法
调用父类的有参构造器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值