大大数据SparkCore二Driver上地Task地生成分配调度.docx
- 文档编号:29097898
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:21
- 大小:142.21KB
大大数据SparkCore二Driver上地Task地生成分配调度.docx
《大大数据SparkCore二Driver上地Task地生成分配调度.docx》由会员分享,可在线阅读,更多相关《大大数据SparkCore二Driver上地Task地生成分配调度.docx(21页珍藏版)》请在冰豆网上搜索。
大大数据SparkCore二Driver上地Task地生成分配调度
大数据:
SparkCore
(二)Driver上的Task的生成、分配、调度
1.什么是Task?
在前面的章节里描述过几个角色,Driver(Client),Master,Worker(Executor),Driver会提交Application到Master进行Worker上的Executor上的调度,显然这些都不是Task.
Spark上的几个关系可以这样理解:
Application:
Application是Driver在构建SparkContent的上下文的时候创建的,就像申报员,现在要构建一个能完成任务的集群,需要申报的是这次需要多少个Executor(可以简单理解为虚拟的机器),每个Executor需要多少CPU,多少内存。
Job:
这是Driver在调用Action的时候生成的Job,让DAGScheduler线程进行最后的调度,代表着这时候RDD的状态分析完了,需要进行最后的Stage分析了,Job并不是提交给Executor运行的,一个Application存在多个Job
Task:
一个Job可以分成多个Task,相当于这些Task分到了一个Group里,这个Group的ID就是JobID
2.Task的类型
Task的类型和Stage相关,关于Stage,以及Stage之间的相关依赖构成任务的不同提交,就不在这篇描述了
ShuffleMapStage转化成ShuffleMapTask
ResultStage转化成为ResultTask
当Spark上的action算子,通过DAG进行提交任务的时候,会通过Stage来决定提交什么类型的任务,具体的实现都在DAGScheduler.Scala的submitMissingTasks方法中。
3.同一个Stage的Task数量
Spark是一个分布式的执行任务的框架,那么同一个Stage的并行任务的拆分就非常的重要,在任务的分解中并不只是stage的步骤的分解,同时也是对同一个Stage中的要分析的数据分解,而对数据的分解直接决定对同一个Stage所提交的任务的数量。
对Stage的任务拆解决定着任务的之间的关系,而对同一个Stage的分析数据进行拆解控制着任务的数量。
比如基于拆解的分析数据的而执行的算子象map,这些任务都是独立的,并没有对数据进行最后的归并和整理,这些task是完全可以进行并行计算的,对同一个Stage的task的数量在Spark上是可以控制的。
在这里以ParallelCollectionRDD为简单的例子,先看DAGScheduler.submitMissingTasks的方法
[java]viewplaincopy
privatedefsubmitMissingTasks(stage:
Stage,jobId:
Int){
logDebug("submitMissingTasks("+stage+")")
//GetourpendingtasksandremembertheminourpendingTasksentry
stage.pendingPartitions.clear()
//Firstfigureouttheindexesofpartitionidstocompute.
valpartitionsToCompute:
Seq[Int]=stage.findMissingPartitions()
。
。
。
。
。
。
。
。
。
。
。
valtasks:
Seq[Task[_]]=try{
stagematch{
casestage:
ShuffleMapStage=>
partitionsToCompute.map{id=>
vallocs=taskIdToLocations(id)
valpart=stage.rdd.partitions(id)
newShuffleMapTask(stage.id,stage.latestInfo.attemptId,
taskBinary,part,locs,stage.latestInfo.taskMetrics,properties,Option(jobId),
Option(sc.applicationId),sc.applicationAttemptId)
}
casestage:
ResultStage=>
partitionsToCompute.map{id=>
valp:
Int=stage.partitions(id)
valpart=stage.rdd.partitions(p)
vallocs=taskIdToLocations(id)
newResultTask(stage.id,stage.latestInfo.attemptId,
taskBinary,part,locs,id,properties,stage.latestInfo.taskMetrics,
Option(jobId),Option(sc.applicationId),sc.applicationAttemptId)
}
}
}catch{
caseNonFatal(e)=>
abortStage(stage,s"Taskcreationfailed:
$e\n${Utils.exceptionString(e)}",Some(e))
runningStages-=stage
return
}
生产task的数量是由valpartitionsToCompute:
Seq[Int]=stage.findMissingPartitions()来决定的,在ShuffleMapStage里
[java]viewplaincopy
overridedeffindMissingPartitions():
Seq[Int]={
valmissing=(0untilnumPartitions).filter(id=>outputLocs(id).isEmpty)
assert(missing.size==numPartitions-_numAvailableOutputs,
s"${missing.size}missing,expected${numPartitions-_numAvailableOutputs}")
missing
}
可以看到具体是由numPartitions来决定的,在来看numPartitions
[java]viewplaincopy
valnumPartitions=rdd.partitions.length
由rdd.partitions来决定的,对ShuffleMapStage来说rdd就是最后一个value类型的transformation的RDD,比如常见的MapPartitionsRDD
在MapPartitionsRDD来说的partitions
[java]viewplaincopy
overridedefgetPartitions:
Array[Partition]=firstParent[T].partitions
是transformation的算子链中的第一个,我们以ParallelCollectionRDD为例子,比如常见的对应的例子:
[java]viewplaincopy
sparkcontext.parallelize(exampleApacheLogs)
在ParallelCollectionRDD中
[java]viewplaincopy
overridedefgetPartitions:
Array[Partition]={
valslices=ParallelCollectionRDD.slice(data,numSlices).toArray
slices.indices.map(i=>newParallelCollectionPartition(id,i,slices(i))).toArray
}
在ParallelCollectionRDD中数据的Partitions是由numSlices来决定的
[java]viewplaincopy
defparallelize[T:
ClassTag](
seq:
Seq[T],
numSlices:
Int=defaultParallelism):
RDD[T]=withScope{
assertNotStopped()
newParallelCollectionRDD[T](this,seq,numSlices,Map[Int,Seq[String]]())
}
numSlices是可以在parallelize函数中传入,而默认使用defaultParallelism的参数控制
[java]viewplaincopy
defdefaultParallelism:
Int={
assertNotStopped()
taskScheduler.defaultParallelism
}
overridedefdefaultParallelism():
Int=backend.defaultParallelism()
在CoarseGrainedSchedulerBackend.scala的类中:
[java]viewplaincopy
overridedefdefaultParallelism():
Int={
conf.getInt("spark.default.parallelism",math.max(totalCoreCount.get(),2))
}
默认的值是受以下控制:
配置文件spark.default.parallelism
totalCoreCount的值:
CoarseGrainedSchedulerBackend是一个executor管理的backend,里面维护着executor的信息,totalCoreCount就是executor汇报上来的核数,注意因为executor汇报自己是在application分配好后发生的,汇报的信息和获取totalCoreCount的线程是异步的,也就是如果executor没有汇报上来,totalCoreCount.get()的值并不准确(根绝Master对executor的分配策略,是无法保证分配多少个executor,在这里spark更依赖executor主动的向driver汇报),这里的策略是无法保证准确的获取executor的核数。
如果没有设置spark.default.parallelism,最小值是2
依赖于rdd.partitions的策略,最后决定task的分配数量。
4.Task的提交和调度分配
在本篇中主要描述集群下的任务调度
4.1Task的提交
在DAGScheduler将一个Stage中所分配的Task形成一个TaskSet进行提交,在TaskSet里所保存的是Task的集合,还有Stage的Id,以及JobId,注意在这里JobId是作为一个优先级的参数,作为后序队列调度的参数。
在TaskSchedulerImpl.scala中
[java]viewplaincopy
overridedefsubmitTasks(taskSet:
TaskSet){
valtasks=taskSet.tasks
logInfo("Addingtaskset"+taskSet.id+"with"+tasks.length+"tasks")
this.synchronized{
valmanager=createTaskSetManager(taskSet,maxTaskFailures)
valstage=taskSet.stageId
valstageTaskSets=
taskSetsByStageIdAndAttempt.getOrElseUpdate(stage,newHashMap[Int,TaskSetManager])
stageTaskSets(taskSet.stageAttemptId)=manager
valconflictingTaskSet=stageTaskSets.exists{case(_,ts)=>
ts.taskSet!
=taskSet&&!
ts.isZombie
}
if(conflictingTaskSet){
thrownewIllegalStateException(s"morethanoneactivetaskSetforstage$stage:
"+
s"${stageTaskSets.toSeq.map{_._2.taskSet.id}.mkString(",")}")
}
schedulableBuilder.addTaskSetManager(manager,manager.taskSet.properties)
if(!
isLocal&&!
hasReceivedTask){
starvationTimer.scheduleAtFixedRate(newTimerTask(){
overridedefrun(){
if(!
hasLaunchedTask){
logWarning("Initialjobhasnotacceptedanyresources;"+
"checkyourclusterUItoensurethatworkersareregistered"+
"andhavesufficientresources")
}else{
this.cancel()
}
}
},STARVATION_TIMEOUT_MS,STARVATION_TIMEOUT_MS)
}
hasReceivedTask=true
}
backend.reviveOffers()
}
将TaskSet封装成TaskSetManager,通过schedulableBuilder去添加TaskSetManager到队列中,在Spark中,有两种形态
FIFOSchedulableBuilder:
单一pool
FairSchedulableBuilder:
多个pool
4.1.1FairSchedulableBuilderpool池
通过fairsscheduler.xml的模版来设置参数来控制pool的调度
[html]viewplaincopy
Arial,Helvetica,sans-serif;">production2 Arial,Helvetica,sans-serif;">"> rgb(51,51,51);font-family: 'SourceCodePro',monospace;font-size: 14px;font-variant-ligatures: normal;orphans: 2;widows: 2;background-color: rgba(128,128,128,0.0470588);">FAIR 参数的定义: name: 调度池的名称,可根据该参数使用指定pool,EX: sc.setLocalProperty("spark.scheduler.pool","production1") weight: 调度池的权重,调度池根据该参数分配资源。 minShare: 调度池需要的最小资源数(CPU核数),公平调度器首先会尝试为每个调度池分配最少minShare资源,然后剩余资源才会按照weight大小继续分配 schedulingMode: 调度池内的调度模式 在TaskSchedulerImpl在submitTasks添加TaskSetManager到pool后,调用了backend.reviveOffers(); [java]viewplaincopy overridedefreviveOffers(){ driverEndpoint.send(ReviveOffers) } [plain]viewplaincopy 是向driver的endpoint发送了reviveoffers的消息,Spark中的许多操作都是通过消息来传递的,哪怕DAGScheduler的线程和endpoint的线程都是同一个Driver进程 4.2Task的分配 Netty的dispatcher线程接受到revievoffers的消息后,CoarseGrainedSchedulerBackend [plain]viewplaincopy caseReviveOffers=> makeOffers() 调用了makeoffers函数 [plain]viewplaincopy privatedefmakeOffers(){ //Filteroutexecutorsunderkilling valactiveExecutors=executorDataMap.filterKeys(executorIsAlive) valworkOffers=activeExecutors.map{case(id,executorData)=> newWorkerOffer(id,executorData.executorHost,executorData.freeCores) }.toIndexedSeq launchTasks(scheduler.resourceOffers(workOffers)) } makeOffers里进行了资源的调度,netty中接收所有的信息,同时也在CoarseGrainedSchedulerBackend中维护着executor的状态map: executorDataMap,executor的状态是executor主动汇报的。 通过scheduler.resourceOffers来进行task的资源分配到executor中 [java]viewplaincopy defresourceOffers(offers: IndexedSeq[WorkerOffer]): Seq[Seq[TaskDescription]]=synchronized{ //Markeachslaveasaliveandrememberitshostname //Alsotrackifnewexecutorisadded varnewExecAvail=false for(o<-offers){ if(! hostToExecutors.contains(o.host)){ hostToExecutors(o.host)=newHashSet[String]() } if(! executorIdToRunningTaskIds.contains(o.executorId)){ hostToExecutors(o.host)+=o.executorId executorAdded(o.executorId,o.host) executorIdToHost(o.executorId)=o.host executorIdToRunningTaskIds(o.executorId)=HashSet[Long]() newExecAvail=true } for(rack<-getRackForHost(o.host)){ hostsByRack.getOrElseUpdate(rack,newHashSet[String]())+=o.host } } //Randomlyshuffleofferstoavoidalwaysplacingtasksonthesamesetofworkers. valshuffledOffers=Random.shuffle(offers) //Buildalistoftaskstoassigntoeachworker. valtasks=shuffledOffers.map(o=>newArrayBuffer[TaskDescription](o.cores)) valavailableCpus=shuffledOffers.map(o=>o.cores).toArray valsortedTaskSets=rootPool.getSortedTaskSetQueue for(taskSet<-edTaskSets){ logDebug("parentName: %s,name: %s,runningTasks: %s".format( taskSet.parent.name,taskSet.name,taskSet.runningTasks)) if(newExecAvail){ taskSet.executorAdded() } } //TakeeachTaskSetinourschedulingorder,andthenofferit
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 大大 数据 SparkCore Driver Task 生成 分配 调度