函数调用分析.docx

上传人:b****7 文档编号:11301805 上传时间:2023-02-26 格式:DOCX 页数:41 大小:163.34KB
下载 相关 举报
函数调用分析.docx_第1页
第1页 / 共41页
函数调用分析.docx_第2页
第2页 / 共41页
函数调用分析.docx_第3页
第3页 / 共41页
函数调用分析.docx_第4页
第4页 / 共41页
函数调用分析.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

函数调用分析.docx

《函数调用分析.docx》由会员分享,可在线阅读,更多相关《函数调用分析.docx(41页珍藏版)》请在冰豆网上搜索。

函数调用分析.docx

函数调用分析

1引言

1.1致谢

作者是在读了“觉先”的博客《Hadoop学习总结之四:

Map-Reduce的过程解析》之后才从宏观上了解HadoopMapReduce模块的工作原理,并且以此为蓝本,写出了本文。

所以,首先在此向“觉先”表示敬意。

另外本文当中可能有很多地方直接引用前述博文,在此特别声明,后面就不一一标注了。

1.2目的

该文档从源代码的级别剖析了Hadoop0.20.2版本的MapReduce模块的运行原理和流程,对JobTracker、TaskTracker的内部结构和交互流程做了详细介绍。

系统地分析了Map程序和Reduce程序运行的原理。

读者在阅读之后会对HadoopMapReduce0.20.2版本源代码有一个大致的认识。

1.3读者范围

如果读者想只是想从原理上更加深入了解HadoopMapReduce运行机制的话,只需要阅读第2章综述即可,该章节要求读者对HadoopMapReduce模型有系统的了解。

如果读者想深入了解HadoopMapReduce的源代码,则需阅读该文档第2、3节。

阅读第3节需要读者熟练掌握Java语言的基本语法,并且对反射机制、动态代理有一定的了解。

同时,还要求读者对于HadoopHDFS和HadoopRPC的基本用法有一定的了解。

另外,属性Hadoop源代码的最好方法是远程调试,有关远程调试的方法请读者去网上自行查阅资料。

1.4术语定义

Job:

用户所需要完成的工作;

Task:

一个Job可以分解为多个Task,每一个Task都是一个独立的运行单元,多个Task并行运行,从而实现了MapReduce的并行计算。

JobTracker:

在HadoopMapReduce模型中负责Job调度的角色;

TaskTracker:

在HadoopMapReduce模型中负责Task调度的角色;

2综述

Hadoop源代码分为三大模块:

MapReduce、HDFS和HadoopCommon。

其中MapReduce模块主要实现了MapReduce模型的相关功能;HDFS模块主要实现了HDFS的相关功能;而HadoopCommon主要实现了一些基础功能,比如说RPC、网络通信等。

在用户使用HadoopMapReduce模型进行并行计算时,用户只需要写好Map函数、Reduce函数,之后调用JobClient将Job提交即可。

在JobTracker收到提交的Job之后,便会对Job进行一系列的配置,然后交给TaskTracker进行执行。

执行完毕之后,JobTracker会通知JobClient任务完成,并将结果存入HDFS中

如图所示,用户提交Job是通过JobClient类的submitJob()函数实现的。

在Hadoop源代码中,一个被提交了的Job由JobInProgress类的一个实例表示。

该类封装了表示Job的各种信息,以及Job所需要执行的各种动作。

在调用submitJob()函数之后,JobTracker会将作业加入到一个队列中去,这个队列的名字叫做jobInitQueue。

然后,在JobTracker中,有一个名为JobQueueTaskScheduler的对象,会不断轮询jobInitQueue队列,一旦发现有新的Job加入,便将其取出,然后将其初始化。

在Hadoop代码中,一个Task由一个TaskInProgress类的实例表示。

该类封装了描述Task所需的各种信息以及Task执行的各种动作。

TaskTracker自从启动以后,会每隔一段时间向JobTracker发送消息,消息的名称为“Heartbeat”。

Heartbeat中包含了该TaskTracker当前的状态以及对Task的请求。

JobTracker在收到Heartbeat之后,会检查该heartbeat的里所包含的各种信息,如果发现错误会启动相应的错误处理程序。

如果TaskTracker在Heartbeat中添加了对Task的请求,则JobTracker会添加相应的指令在对Heartbeat的回复中。

在Hadoop源代码中,JobTracker对TaskTracker的指令称为action,JobTracker对TaskTracker所发送来的Heartbeat的回复消息称为HeartbeatResponse。

在TaskTracker内部,有一个队列叫做TaskQueue。

该中包含了所有新加入的Task。

每当TaskTracker收到HeartbeatResponse后,会对其进行检查,如果其中包含了新的Task,便将其加入到TaskQueue中。

在TaskTracker内部,有两个线程不断轮询TaskQueue,一个是MapLauncher,另一个是ReduceLauncher。

如果发现有新加入的Map任务,MapLauncher便将其取出并且执行。

如果是Reduce任务,ReduceLauncher便将其取出执行。

不论是MapTask还是ReduceTask,当他们被取出之后,都要进行本地化。

本地化的意思就是将所有需要的信息,比如需要运行的jar文件、配置文件、输入数据等等,一起拷贝到本地的文件系统。

这样做的目的是为了方便任务在某台机器上独立执行。

本地化之后,TaskTracker会为每一个task单独创建一个jvm,然后单独运行。

等Task运行完之后,TaskTracker会通知JobTracker任务完成,以进行下一步的动作。

等到所有的Task都完成之后,Job也就完成了,此时JobTracker会通知JobClient工作完成。

3代码详细分析

下面从用户使用Hadoop进行MapReduce计算的过程为线索,详细介绍Task执行的细节,并对HadoopMapReduce的主要代码进行分析。

3.1启动Hadoop集群

Hadoop集群的启动是通过在Master上运行start-all.sh脚本进行的。

运行该脚本之后,Hadoop会配置一系列的环境变量以及其他Hadoop运行所需要的参数,然后在本机运行JobTracker和NameNode。

然后通过SSH登录到所有slave机器上,启动TaskTracker和DataNode。

因为本文只介绍HadoopMapReduce模块,所以NameNode和DataNode的相关知识不再介绍。

3.2JobTracker启动以及Job的初始化

org.apache.hadoop.mapred.JobTracker类实现了HadoopMapReduce模型的JobTracker的功能,主要负责任务的接受,初始化,调度以及对TaskTracker的监控。

JobTracker单独作为一个JVM运行,main函数就是启动JobTracker的入口函数。

在main函数中,有以下两行非常重要的代码:

startTracker(newJobConf());

JobTracker.offerService();

startTracker函数是一个静态函数,它调用JobTracker的构造函数生成一个JobTracker类的实例,名为result。

然后,进行了一系列初始化活动,包括启动RPCserver,启动内置的jetty服务器,检查是否需要重启JobTracker等。

在JobTracker.offerService()中,调用了taskScheduler对象的start()方法。

该对象是JobTracker的一个数据成员,类型为TaskScheduler。

该类型的提供了一系列接口,使得JobTracker可以对所有提交的job进行初始化以及调度。

但是该类型实际上是一个抽象类型,其真正的实现类型为JobQueueTaskScheduler类,所以,taskScheduler.start()方法执行的是JobQueueTaskScheduler类的start方法。

该方法的详细代码如下:

publicsynchronizedvoidstart()throwsIOException{

//调用TaskScheduler.start()方法,实际上没有做任何事情

super.start();

//注册一个JobInProgressListerner监听器

taskTrackerManager.addJobInProgressListener(jobQueueJobInProgressListener

);

eagerTaskInitializationListener.setTaskTrackerManager(taskTrackerManager);

eagerTaskInitializationListener.start();

taskTrackerManager.addJobInProgressListener(eagerTaskInitializationListener)

}

JobQueueTaskScheduler类的start方法主要注册了两个非常重要的监听器:

jobQueueJobInProgressListener和eagerTaskInitializationListener。

前者是JobQueueJobInProgressListener类的一个实例,该类以先进先出的方式维持一个JobInProgress的队列,并且监听各个JobInProgress实例在生命周期中的变化;后者是EagerTaskInitializationListener类的一个实例,该类不断监听jobInitQueue,一旦发现有新的job被提交(即有新的JobInProgress实例被加入),则立即调用该实例的initTasks方法,对job进行初始化。

JobInProgress类的initTasks方法的主要代码如下:

publicsynchronizedvoidinitTasks()throwsIOException{

……

//从HDFS中读取job.split文件从而生成inputsplits

StringjobFile=profile.getJobFile();

PathsysDir=newPath(this.jobtracker.getSystemDir());

FileSystemfs=sysDir.getFileSystem(conf);

DataInputStreamsplitFile=

fs.open(newPath(conf.get("mapred.job.split.file")));

JobClient.RawSplit[]splits;

try{

splits=JobClient.readSplitFile(splitFile);

}finally{

splitFile.close();

}

//maptask的个数就是inputsplit的个数

numMapTasks=splits.length;

//为每个maptasks生成一个TaskInProgress来处理一个inputsplit

maps=newTaskInProgress[numMapTasks];

for(inti=0;i

inputLength+=splits[i].getDataLength();

maps[i]=newTaskInProgress(jobId,jobFile,

splits[i],

jobtracker,conf,this,i);

}

/*

对于maptask,将其放入nonRunningMapCache,是一个Map

List>,也即对于maptask来讲,其将会被分配到其input

split所在的Node上。

在此,Node代表一个datanode或者机架或者数据中

心。

nonRunningMapCache将在JobTracker向TaskTracker分配maptask的

时候使用。

*/

if(numMapTasks>0){

nonRunningMapCache=createCache(splits,maxLevel);

}

//创建reducetask

this.reduces=newTaskInProgress[numReduceTasks];

for(inti=0;i

reduces[i]=newTaskInProgress(jobId,jobFile,

numMapTasks,i,

jobtracker,conf,this);

/*reducetask放入nonRunningReduces,其将在JobTracker向TaskTracker

分配reducetask的时候使用。

*/

nonRunningReduces.add(reduces[i]);

}

//创建两个cleanuptask,一个用来清理map,一个用来清理reduce.

cleanup=newTaskInProgress[2];

cleanup[0]=newTaskInProgress(jobId,jobFile,splits[0],

jobtracker,conf,this,numMapTasks);

cleanup[0].setJobCleanupTask();

cleanup[1]=newTaskInProgress(jobId,jobFile,numMapTasks,

numReduceTasks,jobtracker,conf,this);

cleanup[1].setJobCleanupTask();

//创建两个初始化task,一个初始化map,一个初始化reduce.

setup=newTaskInProgress[2];

setup[0]=newTaskInProgress(jobId,jobFile,splits[0],

jobtracker,conf,this,numMapTasks+1);

setup[0].setJobSetupTask();

setup[1]=newTaskInProgress(jobId,jobFile,numMapTasks,

numReduceTasks+1,jobtracker,conf,this);

setup[1].setJobSetupTask();

tasksInited.set(true);//初始化完毕

……

}

3.3TaskTracker启动以及发送Heartbeat

org.apache.hadoop.mapred.TaskTracker类实现了MapReduce模型中TaskTracker的功能。

TaskTracker也是作为一个单独的JVM来运行的,其main函数就是TaskTracker的入口函数,当运行start-all.sh时,脚本就是通过SSH运行该函数来启动TaskTracker的。

Main函数中最重要的语句是:

newTaskTracker(conf).run();

其中run函数主要调用了offerService函数:

StateofferService()throwsException{

longlastHeartbeat=0;

//TaskTracker进行是一直存在的

while(running&&!

shuttingDown){

……

longnow=System.currentTimeMillis();

//每隔一段时间就向JobTracker发送heartbeat

longwaitTime=heartbeatInterval-(now-lastHeartbeat);

if(waitTime>0){

synchronized(finishedCount){

if(finishedCount[0]==0){

finishedCount.wait(waitTime);

}

finishedCount[0]=0;

}

}

……

//发送Heartbeat到JobTracker,得到response

HeartbeatResponseheartbeatResponse=transmitHeartBeat(now);

……

//从Response中得到此TaskTracker需要做的事情

TaskTrackerAction[]actions=heartbeatResponse.getActions();

……

if(actions!

=null){

for(TaskTrackerActionaction:

actions){

if(actioninstanceofLaunchTaskAction){

//如果是运行一个新的Task,则将Action添加到任务队列中

addToTaskQueue((LaunchTaskAction)action);

}elseif(actioninstanceofCommitTaskAction){

CommitTaskActioncommitAction=(CommitTaskAction)action;

if(!

commitResponses.contains(commitAction.getTaskID())){

commitResponses.add(commitAction.getTaskID());

}

}else{

tasksToCleanup.put(action);

}

}

}

}

returnState.NORMAL;

}

其中transmitHeartBeat函数的作用就是第2章中提到的向JobTracker发送Heartbeat。

其主要逻辑如下:

privateHeartbeatResponsetransmitHeartBeat(longnow)throwsIOException{

//每隔一段时间,在heartbeat中要返回给JobTracker一些统计信息

booleansendCounters;

if(now>(previousUpdate+COUNTER_UPDATE_INTERVAL)){

sendCounters=true;

previousUpdate=now;

}

else{

sendCounters=false;

}

……

//报告给JobTracker,此TaskTracker的当前状态

if(status==null){

synchronized(this){

status=newTaskTrackerStatus(taskTrackerName,localHostname,

httpPort,

cloneAndResetRunningTaskStatuses(

sendCounters),

failures,

maxCurrentMapTasks,

maxCurrentReduceTasks);

}

}

……

//当满足下面的条件的时候,此TaskTracker请求JobTracker为其分配一个新的Task来运行:

//当前TaskTracker正在运行的maptask的个数小于可以运行的maptask的最大个数

//当前TaskTracker正在运行的reducetask的个数小于可以运行的reducetask的最大个数

booleanaskForNewTask;

longlocalMinSpaceStart;

synchronized(this){

askForNewTask=(status.countMapTasks()

status.countReduceTasks()

&&acceptNewTasks;

localMinSpaceStart=minSpaceStart;

}

……

//向JobTracker发送heartbeat,这是一个RPC调用

HeartbeatResponseheartbeatResponse=jobClient.heartbeat(status,

justStarted,askForNewTask,

heartbeatResponseId);

……

returnheartbeatResponse;

}

3.4JobTracker接收Heartbeat并向TaskTracker分配任务

当JobTracker被RPC调用来发送heartbeat的时候,JobTracker的heartbeat(TaskTrackerStatusstatus,booleaninitialContact,booleanacceptNewTasks,shortresponseId)函数被调用:

publicsynchronizedHeartbeatResponseheartbeat(TaskTrackerStatusstatus,

booleaninitialContact,booleanacceptNewTasks,shortresponseId)

throwsIOException{

……

StringtrackerName=status.getTrackerName();

……

shortnewResponseId=(short)(responseId+1);

……

HeartbeatResponseresponse=newHeartbeatResponse(newResponseId,null);

Listactions=newArrayList();

//如果TaskTracker向JobTracker请求一个task运行

if(acceptNewTasks){

TaskTrackerStatustaskTrackerStatus=getTaskTracker(trackerName);

if(taskTrackerStatus==null){

LOG.warn("Unknowntasktrackerpolling;ignoring:

"+trackerName);

}else{

//setup和cleanup的task优先级最高

Listtasks=getSetupAndCleanupTasks(taskTrackerStatus);

if(tasks==null){

//任务调度器分配

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 互联网

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1