spark core原始碼分析2 master啟動流程

五柳-先生發表於2016-01-29

原始碼位置:org.apache.spark.deploy.master.Master.scala

一、main主方法:

[java] view plain copy
  1. def main(argStrings: Array[String]) {  
  2.     SignalLogger.register(log)  
  3.     val conf = new SparkConf  
  4.     val args = new MasterArguments(argStrings, conf)  
  5.     val (actorSystem, _, _, _) = startSystemAndActor(args.host, args.port, args.webUiPort, conf)  
  6.     actorSystem.awaitTermination()  
  7.   }  

解析spark相關的環境變數及方法引數,建立akka actorSystem及ActorRef用於與其它節點的互動,訊息處理類為Master


二、Actor preStart方法

[java] view plain copy
  1. 既然建立了akka,自然最先執行了master的preStart方法。  
  2. override def preStart() {  
  3.     logInfo("Starting Spark master at " + masterUrl)  
  4.     logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")  
  5.     // Listen for remote client disconnection events, since they don't go through Akka's watch()  
[java] view plain copy
  1. //訂閱本身akka生命週期事<span style="font-size:10px;background-color: rgb(255, 255, 255);">件,<span style="font-family: Menlo;">AssociatedEvent,</span><span style="font-family: Menlo;">DisassociatedEvent之類的事件</span></span>  
  2. context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])  
  3. webUi.bind()  
  4. masterWebUiUrl = "http://" + masterPublicAddress + ":" + webUi.boundPort  
  5. //這裡會啟一個定時排程,檢查timeout的worker程式。如果有worker超時,則將狀態置為DEAD,並清理一些記憶體中關於該worker的資訊。如果該worker中有Executor程式,則向driver傳送ExecutorUpdated訊息,表明該Executor也已經不可用了。如果該worker中有Driver程式,且配置driver是可以relaunch的,則重新排程在可用的worker節點上啟動,不然的話就刪除該Driver的記憶體資訊。只有在該worker超時很多次之後,才真正刪除,之前其實只是讓該worker不被選中執行任務而已。  
  6. context.system.scheduler.schedule(0 millis, WORKER_TIMEOUT millis, self, CheckForWorkerTimeOut)  
  7.   
  8. masterMetricsSystem.registerSource(masterSource)  
  9. masterMetricsSystem.start()  
  10. applicationMetricsSystem.start()  
  11. // Attach the master and app metrics servlet handler to the web ui after the metrics systems are  
  12. // started.  
  13. masterMetricsSystem.getServletHandlers.foreach(webUi.attachHandler)  
  14. applicationMetricsSystem.getServletHandlers.foreach(webUi.attachHandler)  
  15.   
  16. //下面是master HA過程,下次會單獨介紹  
  17. val (persistenceEngine_, leaderElectionAgent_) = RECOVERY_MODE match {  
  18.   case "ZOOKEEPER" =>  
  19.     logInfo("Persisting recovery state to ZooKeeper")  
  20.     val zkFactory =  
  21.       new ZooKeeperRecoveryModeFactory(conf, SerializationExtension(context.system))  
  22.     (zkFactory.createPersistenceEngine(), zkFactory.createLeaderElectionAgent(this))  
  23.   case "FILESYSTEM" =>  
  24.     val fsFactory =  
  25.       new FileSystemRecoveryModeFactory(conf, SerializationExtension(context.system))  
  26.     (fsFactory.createPersistenceEngine(), fsFactory.createLeaderElectionAgent(this))  
  27.   case "CUSTOM" =>  
  28.     val clazz = Class.forName(conf.get("spark.deploy.recoveryMode.factory"))  
  29.     val factory = clazz.getConstructor(classOf[SparkConf], classOf[Serialization])  
  30.       .newInstance(conf, SerializationExtension(context.system))  
  31.       .asInstanceOf[StandaloneRecoveryModeFactory]  
  32.     (factory.createPersistenceEngine(), factory.createLeaderElectionAgent(this))  
  33.   case _ =>  
  34.     (new BlackHolePersistenceEngine(), new MonarchyLeaderAgent(this))  
  35. }  
  36. persistenceEngine = persistenceEngine_  
  37. leaderElectionAgent = leaderElectionAgent_  

至此,master主動處理的流程就完了,之後就接受其他的請求來被動處理。


三、接受worker節點的註冊

[java] view plain copy
  1. case RegisterWorker(id, workerHost, workerPort, cores, memory, workerUiPort, publicAddress) =>  
  2. {  
  3.   logInfo("Registering worker %s:%d with %d cores, %s RAM".format(  
  4.     workerHost, workerPort, cores, Utils.megabytesToString(memory)))  
  5.   if (state == RecoveryState.STANDBY) {  
  6.     // ignore, don't send response  
  7.   } else if (idToWorker.contains(id)) {  
  8.     //如果worker id之前已經註冊過,則註冊失敗  
  9.     sender ! RegisterWorkerFailed("Duplicate worker ID")  
  10.   } else {  
  11.     val worker = new WorkerInfo(id, workerHost, workerPort, cores, memory,  
  12.       sender, workerUiPort, publicAddress)  
  13.     if (registerWorker(worker)) { //將worker資訊加入master記憶體中  
  14.       persistenceEngine.addWorker(worker)  
  15.       //向worker傳送RegisteredWorker訊息  
  16.       sender ! RegisteredWorker(masterUrl, masterWebUiUrl)  
  17.       schedule() //排程  
  18.     } else {  
  19.       val workerAddress = worker.actor.path.address  
  20.       logWarning("Worker registration failed. Attempted to re-register worker at same " +  
  21.         "address: " + workerAddress)  
  22.       sender ! RegisterWorkerFailed("Attempted to re-register worker at same address: "  
  23.         + workerAddress)  
  24.     }  
  25.   }  
  26. }  
  27.   
  28. private def schedule(): Unit = {  
  29.   if (state != RecoveryState.ALIVE) { return }  
  30.   // Drivers take strict precedence over executors  
  31.   val shuffledWorkers = Random.shuffle(workers) // Randomization helps balance drivers  
  32.   //將可用的worker隨機化,並將waitingDrivers中的driver啟動  
  33.   for (worker <- shuffledWorkers if worker.state == WorkerState.ALIVE) {  
  34.     for (driver <- waitingDrivers) {  
  35.       if (worker.memoryFree >= driver.desc.mem && worker.coresFree >= driver.desc.cores) {  
  36.         launchDriver(worker, driver)//向worker傳送LaunchDriver訊息  
  37.         waitingDrivers -= driver  
  38.       }  
  39.     }  
  40.   }  
  41.   startExecutorsOnWorkers()//見下面分析  
  42. }  
  43.   
  44. private def startExecutorsOnWorkers(): Unit = {  
  45.   // Right now this is a very simple FIFO scheduler. We keep trying to fit in the first app  
  46.   // in the queue, then the second app, etc.  
  47.   //這個spreadOutApps引數是說明將app儘可能分散在所有的worker中還是儘量分散在一部分worker中  
  48.   if (spreadOutApps) {  
  49.     // Try to spread out each app among all the workers, until it has all its cores  
  50.     for (app <- waitingApps if app.coresLeft > 0) {  
  51.       val usableWorkers = workers.toArray.filter(_.state == WorkerState.ALIVE)  
  52.         .filter(worker => worker.memoryFree >= app.desc.memoryPerExecutorMB &&  
  53.           worker.coresFree >= app.desc.coresPerExecutor.getOrElse(1))  
  54.         .sortBy(_.coresFree).reverse  
  55.       val numUsable = usableWorkers.length  
  56.       val assigned = new Array[Int](numUsable) // Number of cores to give on each node  
  57.       var toAssign = math.min(app.coresLeft, usableWorkers.map(_.coresFree).sum)  
  58.       var pos = 0  
  59.       //while中是為了將app儘可能分散在可用的worker中,在每個worker中啟動一個或者多個Executor  
  60.       while (toAssign > 0) {  
  61.         if (usableWorkers(pos).coresFree - assigned(pos) > 0) {  
  62.           toAssign -= 1  
  63.           assigned(pos) += 1  
  64.         }  
  65.         pos = (pos + 1) % numUsable  
  66.       }  
  67.       // Now that we've decided how many cores to give on each node, let's actually give them  
  68.       for (pos <- 0 until numUsable if assigned(pos) > 0) {  
  69.         allocateWorkerResourceToExecutors(app, assigned(pos), usableWorkers(pos))  
  70.       }  
  71.     }  
  72.   } else {  
  73.     // Pack each app into as few workers as possible until we've assigned all its cores  
  74.     for (worker <- workers if worker.coresFree > 0 && worker.state == WorkerState.ALIVE) {  
  75.       for (app <- waitingApps if app.coresLeft > 0) {  
  76.         allocateWorkerResourceToExecutors(app, app.coresLeft, worker)  
  77.       }  
  78.     }  
  79.   }  
  80. }  
  81.   
  82. private def allocateWorkerResourceToExecutors(  
  83.     app: ApplicationInfo,  
  84.     coresToAllocate: Int,  
  85.     worker: WorkerInfo): Unit = {  
  86.   val memoryPerExecutor = app.desc.memoryPerExecutorMB  
  87.   val coresPerExecutor = app.desc.coresPerExecutor.getOrElse(coresToAllocate)  
  88.   var coresLeft = coresToAllocate  
  89.   while (coresLeft >= coresPerExecutor && worker.memoryFree >= memoryPerExecutor) {  
  90.     val exec = app.addExecutor(worker, coresPerExecutor)  
  91.     coresLeft -= coresPerExecutor  
  92.     //向worker傳送LaunchExecutor訊息,並向driver傳送ExecutorAdded訊息  
  93.     launchExecutor(worker, exec)  
  94.     app.state = ApplicationState.RUNNING  
  95.   }  
  96. }  

除HA相關訊息之外,還可接收如下訊息,功能處理其實也都比較簡單,後續會結合Job的提交過程逐步分析。。
case RequestSubmitDriver(description)//請求提交Driver訊息,記錄Driver的資訊並排程
case RequestKillDriver(driverId)
case RequestDriverStatus(driverId)
case RegisterApplication(description)//提交Application,記錄Application的資訊並排程
case ExecutorStateChanged(appId, execId, state, message, exitStatus)
case DriverStateChanged(driverId, state, exception)

case Heartbeat(workerId)//心跳,用於worker節點的保活

轉載:http://blog.csdn.net/yueqian_zhu/article/details/47907095

相關文章