hadoop-mapreduce中maptask运行分析

MapTask运行通过执行.run方法:

1.生成TaskAttemptContextImpl实例,此实例中的Configuration就是job本身。

2.得到用户定义的Mapper实现类,也就是map函数的类。

3.得到InputFormat实现类。

4.得到当前task对应的InputSplit.

5.通过InputFormat,得到对应的RecordReader

6.生成RecordWriter实例,

如果reduce个数为0,生成为MapTask.NewDirectOutputCollector

如果reduce个数不为0,但肯定是一个大于0的数,生成MapTask.NewOutputCollector

如果是有reduce的情况,在collector中会生成一个buffercollector用来进行内存排序。

通过mapreduce.job.map.output.collector.class配置,默认为MapTask.MapOutputBuffer

MapOutputBuffer中:

通过mapreduce.map.sort.spill.percent配置内存flush的比值,默认为0.8

spill的中文意思是溢出。

通过mapreduce.task.io.sort.mb配置内存bufer的大小,默认是100mb

通过mapreduce.task.index.cache.limit.bytes配置(还不知道是做什么的),默认为1024*1024

提示,这个配置是用来cache进行spill操作的index的大小。当spillindex达到此值的时候,

需要写入spillindex的文件。

通过map.sort.class配置排序实现类,默认为QuickSort,快速排序

通过mapreduce.map.output.compress.codec配置map的输出的压缩处理程序。

通过mapreduce.map.output.compress配置map输出是否启用压缩。默认为false.

MapOutputBuffer实例生成部分结束。


在生成MapTask.NewOutputCollector同时,会

检查是否用户有定义的Partitioner,默认是HashPartitioner

如果生成的实例为MapTask.NewDirectOutputCollector,也就是没有Reduce的情况下,

不执行排序操作也不执行buffer的缓冲操作,直接写入到output的文件中。

通过OutputFormatRecordWriter





以下是mapper.run方法的执行代码:

publicvoidrun(Context context) throwsIOException, InterruptedException {

setup(context);

try{

while(context.nextKeyValue()) {

map(context.getCurrentKey(),context.getCurrentValue(), context);

}

}finally{

cleanup(context);

}

}

由上面的代码可以看出,map运行时,会执行一次setup函数,完成时会执行一次cleanup函数。

中间只要有值就会调用map函数。

其中run中传入的context生成由来:

if(job.getNumReduceTasks() == 0) {

output =

newNewDirectOutputCollector(taskContext,job, umbilical, reporter);

}else{

output = newNewOutputCollector(taskContext,job, umbilical, reporter);

}

MapContextImpl实例,包含input(RecordReader)output,也就是上面提到的collector.

org.apache.hadoop.mapreduce.MapContext<INKEY,INVALUE, OUTKEY, OUTVALUE>

mapContext =

newMapContextImpl<INKEY, INVALUE, OUTKEY, OUTVALUE>(job,getTaskID(),

input, output,

committer,

reporter, split);

WrappedMapper.Context实例。包含MapContextImpl实例。

org.apache.hadoop.mapreduce.Mapper<INKEY,INVALUE,OUTKEY,OUTVALUE>.Context

mapperContext =

newWrappedMapper<INKEY, INVALUE, OUTKEY, OUTVALUE>().getMapContext(

mapContext);



接着看mapper.run中的context.nextKeyValue()函数:

调用WrappedMapper.Context.nextKeyValue()函数,-->

调用MapContextImpl.nextKeyValue函数,-->

调用RecordReader.nextKeyValue函数,RecordReader不在说明。


map函数对过程处理完成后,会通过context.write写入分析的数据,

context.write(word,one);


看看此部分是如何执行的:

调用WrappedMapper.Context.write-->

调用MapContextImpl.write-->TaskInputOutputContextImpl.write-->

MapTask.NewOutputCollector.write/MapTask.NewDirectOutputCollector.write


MapTask.NewDirectOutputCollector.write:

这个里面没什么可以说的,直接写入到输出文件中。

NewDirectOutputCollector(MRJobConfigjobContext,

JobConf job,TaskUmbilicalProtocol umbilical, TaskReporter reporter)

throwsIOException, ClassNotFoundException, InterruptedException {

............................................

out= outputFormat.getRecordWriter(taskContext);

............................................

}


写入函数的定义

publicvoidwrite(K key, V value)

throwsIOException, InterruptedException {

reporter.progress();

longbytesOutPrev = getOutputBytes(fsStats);

直接写入文件。

out.write(key,value);

longbytesOutCurr = getOutputBytes(fsStats);

fileOutputByteCounter.increment(bytesOutCurr- bytesOutPrev);

mapOutputRecordCounter.increment(1);

}



重点来看看MapTask.NewOutputCollector.write这部分的实现:

通过Partitioner来生成reducepartition值,调用MapOutputBuffer.collect函数。

也就是写入到buffer中。

publicvoidwrite(K key, V value) throwsIOException, InterruptedException {

collector.collect(key,value,

partitioner.getPartition(key,value, partitions));

}



MapOutputBuffer.collector:


publicsynchronized voidcollect(K key, V value, final intpartition

)throwsIOException {

reporter.progress();

检查传入的key的类型是否是jobMapOutputKeyClass的值

if(key.getClass() != keyClass){

thrownewIOException("Type mismatch in keyfrom map: expected "

+keyClass.getName()+ ", received "

+key.getClass().getName());

}

检查传入的value的类型是否是jobMapOutputValueClass的值。

if(value.getClass() != valClass){

thrownewIOException("Type mismatch in valuefrom map: expected "

+valClass.getName()+ ", received "

+value.getClass().getName());

}

检查partition是否在指定的范围内。

if(partition < 0 || partition >= partitions){

thrownewIOException("Illegal partition for" + key + "(" +

partition + ")");

}

检查sortSpillException的值是否为空,如果不为空,表示有spill错误,throwioexception

checkSpillException();

把可写入的buffer的剩余部分减去一个固定的值,并检查可用的buffer是否达到了sortspill的值

默认是buffer0.8的大小,如果buffer0.8METASIZE取于不等于0时,

得到的值可能会比0.8METASIZE这么一点。

bufferRemaining-= METASIZE;

if(bufferRemaining<= 0) {

执行spill操作,这部分等下再进行分析

//start spill if the thread is not running and the soft limit has been

//reached

spillLock.lock();

try{

......................此部分代码先不看

} finally{

spillLock.unlock();

}

}


try{

第一次进入时,bufindex的值为0,以后的每一次是key.len+1+value.len+1的值增加。

//serialize key bytes into buffer

intkeystart = bufindex;

key写入到此实例中的一个BlockingBuffer类型的属性bb中。这是一个buffer.

在写入时把bufferRemaining的值减去key.length的长度。这里面也会检查buffer是否够用

key写入到kvbuffer中,同时把bufindex的值加上key.lengthKvbuffer就是具体的buffer.

在执行写入key/value时,首先是先把bufferRemaining的值减去key.length/value.length的长度。

同时检查此时

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值