spark核心(下)——job任務提交原始碼解析

平平無奇小碼農發表於2020-12-16
Driver ---->main ----> SparkContext --->  RDD ---> RDD ---->RDD
		---RDD.collect()

第一部分: SparkContext的構造
SparkContext  (Driver構造)
	核心屬性:  var _env: SparkEnv :  封裝了Spark所有的環境資訊(cache,序列化器,BM)
				_env = createSparkEnv(_conf, isLocal, listenerBus)
				
	
				var _taskScheduler: TaskScheduler  //任務排程器,目的將Task傳送給Executor執行
				 val (sched, ts) = SparkContext.createTaskScheduler(this, master, deployMode)    //528行
						--case masterUrl =>
							--val scheduler = cm.createTaskScheduler(sc, masterUrl)   // YARN Cluster模式
							-- val backend = cm.createSchedulerBackend(sc, masterUrl, scheduler)  //YarnClusterSchedulerBackend
								--YarnClusterSchedulerBackend 父類 YarnSchedulerBackend 的構造器中 
								-- 爺爺類 CoarseGrainedSchedulerBackend 的構造器
										// 名稱: CoarseGrainedScheduler  端點型別: DriverEndpoint
									--屬性 val driverEndpoint = rpcEnv.setupEndpoint(ENDPOINT_NAME, createDriverEndpoint())
							
							--cm.initialize(scheduler, backend)     // 初始化排程器要使用的排程池
								--scheduler.asInstanceOf[TaskSchedulerImpl].initialize(backend)
									--initialize   //TaskSchedulerImpl.initialize
									//根據配置的排程模式,來建立對應的排程池,排程模式取決於配置spark.scheduler.mode,預設為FIFO
										--  schedulableBuilder =new FIFOSchedulableBuilder(rootPool)
										--schedulableBuilder.buildPools() 
										
							-- (backend, scheduler)  
				
				
				private var _dagScheduler: DAGScheduler   //將RDD根據依賴劃分階段,在WebUI上可以檢視DAG圖,DAG是Spark區分於Hadoop的根本特徵
					--val eventProcessLoop = new DAGSchedulerEventProcessLoop(this)
					--eventProcessLoop.start()   //在構造DAGScheduler時就執行
						-- onStart()   //{}
						--eventThread.start()
							----eventThread.run(){
								while (!stopped.get) {
								val event = eventQueue.take()    //從事件佇列中取出一個事件
								try {
									onReceive(event)            //處理事件
										--doOnReceive(event)
											--case JobSubmitted
												=>dagScheduler.handleJobSubmitted  // 見第三部分
								} 
							}

第二部分:  呼叫行動運算元,提交Job
RDD.collect
	--val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
		-- dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get)
			--val waiter = submitJob(rdd, func, partitions, callSite, resultHandler, properties)   // DAGScheduler 提交Job
				--val jobId = nextJobId.getAndIncrement()  //生成JobID
				//  要求分割槽不能為空,如果分割槽為空,就立刻退出,如果有分割槽,此時
					
				--eventProcessLoop.post(JobSubmitted)         // 向  事件處理迴圈中 提交一個事件,將事件提交到佇列的尾部,之後會有執行緒自動處理事件
					--eventQueue.put(event)      
					
					
第三部分: DAGScheduler.handleJobSubmitted 處理提交的Job
--dagScheduler.handleJobSubmitted
	--var finalStage: ResultStage = null
	--finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)
		--val parents = getOrCreateParentStages(rdd, jobId)    // 獲取當前階段的父階段(不是祖先階段)
			// 獲取當前RDD直接的有shuffle依賴的父親
			-- getShuffleDependencies(rdd).map { shuffleDep =>
				  getOrCreateShuffleMapStage(shuffleDep, firstJobId)   // 一旦查詢父RDD的shuffle依賴資訊,此時就建立一個ShuffleMapStage
					// 先從之前建立的Map集合中取
					--shuffleIdToMapStage.get(shuffleDep.shuffleId) match {
						  case Some(stage) =>
							stage    //取出就返回

						  case None =>   //沒取到
								// 將當前ShuffleMapStage 的所有缺失的 祖先ShuffleMapStage全部建立
							getMissingAncestorShuffleDependencies(shuffleDep.rdd).foreach { dep =>
								// 判斷祖先 ShuffleMapStage是否確實
							  if (!shuffleIdToMapStage.contains(dep.shuffleId)) {
							  // 先建立 祖先 ShuffleMapStage
								createShuffleMapStage(dep, firstJobId)
							  }
							}
							// Finally, create a stage for the given shuffle dependency.
							// 最後,再建立當前 rdd 直接 父RDD的ShuffleMapStage
							createShuffleMapStage(shuffleDep, firstJobId)
								-- val numTasks = rdd.partitions.length     //numTasks=分割槽數
								--val parents = getOrCreateParentStages(rdd, jobId)
								--val stage = new ShuffleMapStage
								--shuffleIdToMapStage(shuffleDep.shuffleId) = stage  
							
							// 所有的ShuffleMapStage全部結束
						}
				}.toList
				
		-- val stage = new ResultStage(id, rdd, func, partitions, parents, jobId, callSite)
		// 所有的Stage全部結束, Stage 要麼是 ShuffleMapStage,要麼是 ResultStage,最後一個是ResultStage
		
	
	// 如果是ResultStage,提交result-job,如果是ShuffleMapStage,執行 map-shuffle job
	-- val job = new ActiveJob(jobId, finalStage, callSite, listener, properties)
	-- finalStage.setActiveJob(job)
	--submitStage(finalStage)       // 提交最後的階段,DAGScheduler完成工作
		// 獲取當前最後階段是否有沒有提交的缺失的父階段,將缺失的父階段按照ID升序排序,ID小的會被優先提交
		--val missing = getMissingParentStages(stage).sortBy(_.id)   
		--if (missing.isEmpty){   //沒有缺失的父階段,都已經提交或壓根就沒有父階段
			--submitMissingTasks(stage, jobId.get)  //提交當前階段,封裝Task來執行這個階段的邏輯
				--val tasks: Seq[Task[_]]=try{
					case stage: ShuffleMapStage =>
						partitionsToCompute.map{
							 new ShuffleMapTask         //如果是ShuffleMapStage,每個分割槽都建立一個 ShuffleMapTask
						}
					
					case stage: ResultStage =>
					partitionsToCompute.map{
							 new ResultTask         //如果是ShuffleMapStage,每個分割槽都建立一個 ResultTask
						}
					}
					
				--if (tasks.nonEmpty){
					taskScheduler.submitTasks(new TaskSet)   // 一個Stage如果有多個分割槽,就有多個Task,這些Task會放入一個TaskSet
															// 一個stage對應一個 TaskSet
				}
			}else {
          for (parent <- missing) {
            submitStage(parent)     // 只要有沒有提交的父階段,就先提交父階段
          }
		  waitingStages += stage  // 將當前提交的父階段放入要等待執行的階段中
		
		
		
第四部分: TaskScheduler 負責排程 Task 傳送到Executor進行計算
TaskScheduler
	--submitTasks(new TaskSet)
		--val tasks = taskSet.tasks   // 從TaskSet中取出 tasks
		--val manager = createTaskSetManager(taskSet, maxTaskFailures)  
			// 每個Stage 會建立一個 TaskSet,每個TaskSet又會建立一個對應的TaskSetManager
			--new TaskSetManager(this, taskSet, maxTaskFailures, blacklistTrackerOpt) 
			
		--val stage = taskSet.stageId   // 獲取當前階段ID
		--schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties)  // 將當前TaskSetManager新增到排程池中
		
		--backend.reviveOffers()  //  從排程池中,取出TaskSetManager,開始排程
			-- driverEndpoint.send(ReviveOffers)       //CoarseGrainedSchedulerBackend.reviveOffers
			
			
第五部分 : DriverEndpoint 處理 ReviveOffers訊息
DriverEndpoint
	--receive
		-- case ReviveOffers => makeOffers()
			--val taskDescs={
				--val activeExecutors = executorDataMap.filterKeys(isExecutorActive)  // 篩選當前active狀態的所有的executors
				--val workOffers = activeExecutors.map{
					case (id, executorData) => new WorkerOffer   // 每個active的executor都建立一個 WorkerOffer
					}
					
				//此方法由叢集呼叫,在叢集的slave階段上準備資源。 根據TaskSet的優先順序,以輪詢的方式傳送到叢集,以保證負載均衡
				--scheduler.resourceOffers(workOffers)  
					--val sortedTaskSets = rootPool.getSortedTaskSetQueue.filterNot(_.isZombie)
						--val sortedTaskSetQueue = new ArrayBuffer[TaskSetManager]  // 建立一個排序後的TaskSetManager的集合
						// 從rootPool中獲取schedulableQueue,之後將佇列中的TaskSet,呼叫 排程演算法 進行排序
						// 預設使用 FIFOSchedulingAlgorithm() 的比較器排序,stageId 小的排在前面
						--val sortedSchedulableQueue =
								schedulableQueue.asScala.toSeq.sortWith(taskSetSchedulingAlgorithm.comparator)
								
						// 將排好序的 TaskSetManager取出,依次放入sortedTaskSetQueue
						--sortedTaskSetQueue
					
					// 對已經排好序的TaskSetManager 進行各種處理(看哪個executor適合運算這個TaskSetManager)
					// 每個TaskSetManager 都有一個屬性 var myLocalityLevels = computeValidLocalityLevels()
					// 在計算時,import TaskLocality.{PROCESS_LOCAL, NODE_LOCAL, NO_PREF, RACK_LOCAL, ANY}
					// 所有的 TaskSetManager的myLocalityLevels 都有ANY,之後看是否複合其他條件,加入對應的級別
					-- for (currentMaxLocality <- taskSet.myLocalityLevels) {  //嘗試從TaskSetManager的所有本地化級別中獲取最優的
					
					-- return tasks    // 返回設定和處理後的tasks
					}
					
				--if (taskDescs.nonEmpty) {
					launchTasks(taskDescs)     // 傳送Task
						--val serializedTask = TaskDescription.encode(task)  //序列化每個Task的描述
						-- val executorData = executorDataMap(task.executorId)  // 獲取當前Tasks應該發往的executor的資訊
						// 獲取要傳送的exeuctor的通訊端點的引用,傳送 LaunchTask資訊,同時將 TaskDesc也傳送過去
						-- executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))
				}
				
第六部分:  通訊中的Executor收到訊息進行處理
CoarseGrainedExecutorBackend.receive
	-- case LaunchTask(data) =>
			--val taskDesc = TaskDescription.decode(data.value)  // 將TaskDesc反序列化
			--executor.launchTask(this, taskDesc)
				--val tr = new TaskRunner(context, taskDescription)   // 建立一個執行Task的執行緒
				--threadPool.execute(tr)     // 執行執行緒,執行tr的run方法
				// 從Task的描述資訊中,反序列化取出Task
					-- task = ser.deserialize[Task[Any]](
							taskDescription.serializedTask, Thread.currentThread.getContextClassLoader)
					--task.run
						-- val taskContext = new TaskContextImpl
							--runTask(context)
								//不同型別的Task執行自己的處理邏輯
								// 將當前stage的結果寫出
								-- ShuffleMapTask
									--dep.shuffleWriterProcessor.write(rdd, dep, mapId, context, partition)
										-- var writer: ShuffleWriter[Any, Any] = null
										-- val manager = SparkEnv.get.shuffleManager
										// shuffle以什麼形式寫出,由shuffleManager決定
										--writer = manager.getWriter
										// 使用RDD的迭代器,寫出當前Stage最後一個RDD中的資料
										--writer.write(
												// rdd.iterator  是個方法
												rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]])
												      // 判斷是否使用了快取
												-- if (storageLevel != StorageLevel.NONE) { 
															// 從快取中獲取RDD,如果快取中沒有就計算得到
													  getOrCompute(split, context)
													} else {
														// 嘗試從ck目錄中獲取RDD,如果沒有就計算得到
													  computeOrReadCheckpoint(split, context)
														--if (isCheckpointedAndMaterialized) {
															// 如果ck目錄有,獲取CheckPointRDD,呼叫它的iterator方法
															  firstParent[T].iterator(split, context)
															} else {
																//計算得到
															  compute(split, context)
																
																-- textFile  new HadoopRDD   :  compute的邏輯就是返回迭代當前這片資料的迭代器
																-- map   MapPartitionsRDD   : compute 
																					 f(context, split.index, firstParent[T].iterator(split, context))
																					  --(_, _, iter) => iter.map(cleanF)  (context, split.index, firstParent[T].iterator(split, context))
																					  -- (_, _, iter) => iter.map( word => (word, 1))  (context, split.index, firstParent[T].iterator(split, context))
															}	
													}
													
												
								// 執行行動運算元最終的邏輯
								-- ResultTask 
									// func 是呼叫行動運算元的RDD 行動運算元的邏輯
									// 以collect 為例,func: (iter: Iterator[T]) => iter.toArray
									// rdd.iterator(partition, context) : 呼叫 ResultStage 最後一個呼叫行動運算元的RDD的compute()
									// 
									--func(context, rdd.iterator(partition, context))
			

相關文章