spark core原始碼分析4 worker啟動流程

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

原始碼位置:org.apache.spark.deploy.worker.Worker.scala

首先檢視worker的main方法,與master類似,建立sparkConf,引數解析,以及構造worker物件並建立ActorRef用於對外或者本身的資訊互動。這裡masters引數可以設定多個

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

程式起來後,同樣是先執行akka 的preStart方法
  1. override def preStart() {  
  2.   assert(!registered)  
  3.   logInfo("Starting Spark worker %s:%d with %d cores, %s RAM".format(  
  4.     host, port, cores, Utils.megabytesToString(memory)))  
  5.   logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")  
  6.   logInfo("Spark home: " + sparkHome)  
  7.   createWorkDir()//建立worker內部工作目錄  
  8.   //訂閱akka生命週期事件  
  9.   context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])  
  10.   //是否額外的啟動一個shuffle服務,確保被executor所讀寫的shuffle檔案在executor退出後被儲存,可配  
  11.   shuffleService.startIfEnabled()  
  12.   webUi = new WorkerWebUI(this, workDir, webUiPort)  
  13.   webUi.bind()  
  14.   registerWithMaster()//最重要的動作了,見下面  
  15.   
  16.   metricsSystem.registerSource(workerSource)  
  17.   metricsSystem.start()  
  18.   // Attach the worker metrics servlet handler to the web ui after the metrics system is started.  
  19.   metricsSystem.getServletHandlers.foreach(webUi.attachHandler)  
  20. }  
向Master註冊自己
  1. private def registerWithMaster() {  
  2.   // DisassociatedEvent may be triggered multiple times, so don't attempt registration  
  3.   // if there are outstanding registration attempts scheduled.  
  4.   registrationRetryTimer match {  
  5.     case None =>  
  6.       registered = false  
  7.       //這裡向所有的master actorRef傳送RegisterWorker訊息,上幾節有講master收到該訊息後,如果成功處理會反饋RegisteredWorker訊息,不成功會傳送RegisterWorkerFailed訊息  
  8.       tryRegisterAllMasters()  
  9.       connectionAttemptCount = 0  
  10.       //這裡在一定時間之後會進入ReregisterWithMaster,裡面會判斷是否已註冊,如果沒有會再次傳送註冊資訊。這個是否註冊的狀態是由master反饋回來的  
  11.       registrationRetryTimer = Some {  
  12.         context.system.scheduler.schedule(INITIAL_REGISTRATION_RETRY_INTERVAL,  
  13.           INITIAL_REGISTRATION_RETRY_INTERVAL, self, ReregisterWithMaster)  
  14.       }  
  15.     case Some(_) =>  
  16.       logInfo("Not spawning another attempt to register with the master, since there is an" +  
  17.         " attempt scheduled already.")  
  18.   }  
  19. }  
看worker收到master的RegisteredWorker訊息會怎麼做?這裡要說一點,worker要註冊時並不知道哪臺是主,哪臺是備,所以向所有配置的master都傳送註冊資訊。主備都收到worker的註冊資訊之後,只有主才會反饋,並帶上自己的masterUrl資訊,worker以此來認定主master的actorRef用於真正的資訊互動
worker要通過心跳來保持與master的時刻連通,所以註冊成功之後,有一個connected標記是否連線正常,在changeMaster方法內部設定connected = true
  1. <pre name="code" class="java">case RegisteredWorker(masterUrl, masterWebUiUrl) =>  
  2.   logInfo("Successfully registered with master " + masterUrl)  
  3.   registered = true //狀態設定為已註冊,不然的話,一定時間過後,會發起ReregisterWithMaster而重複註冊  
  4.   changeMaster(masterUrl, masterWebUiUrl)//這裡是將主master的資訊儲存  
  5.   
  6.   //在註冊成功之後,才開啟定時器向master傳送心跳  
  7.   context.system.scheduler.schedule(0 millis, HEARTBEAT_MILLIS millis, self, SendHeartbeat)  
  8.   //定時器清理workDir下很久都沒有更新的且app也不在執行狀態的目錄  
  9.   if (CLEANUP_ENABLED) {  
  10.     logInfo(s"Worker cleanup enabled; old application directories will be deleted in: $workDir")  
  11.     context.system.scheduler.schedule(CLEANUP_INTERVAL_MILLIS millis,  
  12.       CLEANUP_INTERVAL_MILLIS millis, self, WorkDirCleanup)  
  13.   }  
如果收到RegisterWorkerFailed訊息,則退出


下面看master接受到worker的心跳之後如何處理
由於worker註冊時,master已經將workerId存入idToWorker中,所以這裡走Some分支。很簡單,只是更新該worker的一個時間戳。這裡有必要說明一下None分支,在註冊訊息到達後,在master 的idToWorker和workers中都會儲存,但是當master檢測到worker超時時,將worker從idToWorker中刪除,這樣新的任務就選不了該worker了,但不刪除workers中的。workers中的只會在間隔很長一段時間之後仍然沒有心跳上來,才說明該worker真正無法再工作了,再從workers中刪除。這裡的None分支就是應對超時過後,心跳又繼續上來了,就向worker傳送重新註冊的訊息ReconnectWorker
  1. case Heartbeat(workerId) => {  
  2.   idToWorker.get(workerId) match {  
  3.     case Some(workerInfo) =>  
  4.       workerInfo.lastHeartbeat = System.currentTimeMillis()  
  5.     case None =>  
  6.       if (workers.map(_.id).contains(workerId)) {  
  7.         logWarning(s"Got heartbeat from unregistered worker $workerId." +  
  8.           " Asking it to re-register.")  
  9.         sender ! ReconnectWorker(masterUrl)  
  10.       } else {  
  11.         logWarning(s"Got heartbeat from unregistered worker $workerId." +  
  12.           " This worker was never registered, so ignoring the heartbeat.")  
  13.       }  
  14.   }  
  15. }  

至此,worker啟動流程以及主動傳送的訊息介紹完了,剩下的都是被動接收並處理的流程,在之後結合具體job介紹。。。

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

相關文章