目錄
1.2 startRpcEnvAndEndpoint(...)
一、Master啓動主流程
1.1 Master main方法
執行 $SPARK_HOME/sbin/start_master.sh 腳本時,執行的是Master.scala中的main方法。下面我們來看代碼,看Master啓動過程中都幹了哪些事:
1.2 startRpcEnvAndEndpoint(...)
截圖中有註釋,我就不一一說了。我們看重點:startRpcEnvAndEndpoint(...)
1.3 rpcEnv.setupEndpoint(...)
這裏我們重點看一下第3步: val masterEndpoint = rpcEnv.setupEndpoint(...)
這個方法需要兩個參數:endPoint的名字,這裏是ENDPOINT_NAME,也就是“Master”,還有一個endPoint對象,也就是Master對象,Master對象就是在此處實例化 的(稍後講解)。
rpcEnv.setupEndpoint(...)最終調用的是dispatcher.registerRpcEndpoint(name, endpoint),我們來看一下這個方法:
這裏第4步和第7步需要強調一下:
第4步:向Dispatcher中註冊EndPoint時,需要傳入一個EndPointData,初始化EndPointData時會創建該EndPoint的Inbox,而InBox的初始化完成後,會向InBox的messages中添加一個OnStart信息(OnStart是InBox處理的第一條信息)。
org/apache/spark/rpc/netty/Inbox.scala:80
第7步:向Dispatcher的receviers中添加一個EndPointData,這個data是爲OnStart準備的。
rpcEnv.setupEndpoint(...)方法執行完後,返回一個endpointRef,Master.scala中的啓動相關的代碼就看完了(Master初始化還沒看),在看Master對象初始化之前我們先回頭看一下Dispatcher。
二、Master啓動需要注意的地方
2.1 Dispatcher的線程池
還記得Dispatcher的線程池嗎?這裏我們再來看一下:
2.2 Inbox.process(...)
Dispatcher初始化了一個線程池,並啓動。我們在看一下MessageLoop
Master的註冊過程中向dispatcher的receivers中添加了一個EndPointData,Inbox中添加了一個OnStart。所以線程運行到if(data == PoisonPill)時,條件不成立,進入第3步,執行data.inbox.process(Dispatcher.this)。我們再來看看這個方法:
首先取出inbox中的數據,做非空判斷,處理線程數問題。下面開始處理數據:
我們看一下3.3 OnStart信息的處理。InBox初始化時往messages中添加了一個OnStart,所以Master第一次啓動時,Dispatcher-EndPoint-InBox中存的第一條信息是OnStart。3.2.1調用的onstart()方法即是Master的onStart方法。也就是說,在Master初始化完成後,便開始執行其onStart()方法。
三、Master的初始化過程
下面我們看一下Master的初始化過程,隨後再看下Master的onStart()方法都幹了哪些事:
Master是在Master.scala startRpcEnvAndEndpoint(...)方法中向env註冊時通過new關鍵字初始化的。我們直接看Master類有哪些屬性,之後再看其onStart方法。
3.1 參數初始化
Master初始化時初始化了很多屬性,這裏我們撿幾個重要的列一下:
// hadoop配置
private val hadoopConf = SparkHadoopUtil.get.newConfiguration(conf)
// Worker超時時間,默認60s
private val WORKER_TIMEOUT_MS = conf.getLong("spark.worker.timeout", 60) * 1000
// executor最大重試次數(默認10次,也就是說最多執行11次)
private val MAX_EXECUTOR_RETRIES = conf.getInt("spark.deploy.maxExecutorRetries", 10)
// worker列表,裏面存放worker的id,地址,端口,核心數,內存,worker引用,webUi地址
val workers = new HashSet[WorkerInfo]
// 等待中的apps(已提交未處理)
private val waitingApps = new ArrayBuffer[ApplicationInfo]
// app列表
val apps = new HashSet[ApplicationInfo]
// 處理完成的apps
private val completedApps = new ArrayBuffer[ApplicationInfo]
// 下一個App編號
private var nextAppNumber = 0
// 驅動
private val drivers = new HashSet[DriverInfo]
// 已完成的driver
private val completedDrivers = new ArrayBuffer[DriverInfo]
// Drivers currently spooled for scheduling
private val waitingDrivers = new ArrayBuffer[DriverInfo]
// 下一個驅動的編號
private var nextDriverNumber = 0
// After onStart, webUi will be set
private var webUi: MasterWebUI = null
// master url
private val masterUrl = address.toSparkURL
// master weburl
private var masterWebUiUrl: String = _
// master節點狀態
private var state = RecoveryState.STANDBY
// 如果沒有指定,則默認最大核心數爲Int最大值
private val defaultCores = conf.getInt("spark.deploy.defaultCores", Int.MaxValue)
3.2 Master的onStart()方法
下面我們來看下Master的onstart方法幹了哪些事:
我們看一下 self.send(CheckForWorkerTimeOut) :
這個self是RpcEndpointRef,這裏也就是Master的引用,我把實現貼在下面:
NettyRpcEnv.scala
Dispatcher發送出一條OneWayMessage(不需要回復的)。我們接着往下看:
看一下data.inbox.post(message):
這時,消息已經存放在Master的inbox中,當Dispatcher的輪詢線程執行到時,會調用inbox.process處理這條消息,inbox.process()方法這裏不再細說(2.1.2),我們直接看對應片段(由dispatcher.postOneWayMessage(message)知道是一條OneWayMessage):
此處的endpoint.receive即是Master的receive方法,我們看一下:
Master的receive方法根據接收到的數據類型進行模式匹配,我們看下CheckForWorkerTimeOut數據類型幹了什麼:
我們看下timeOutDeadWorkers()
至於worker刪除的細節,這裏就不看了。我們回到Master的onStart方法。
至此,Master的啓動已經完成。我們簡單總結一下:
首先,初始化了Master rpcEnv環境,初始化了Rpc通信的相關組件,並在Dispatcher和Inbox中添加了啓動數據。初始化一個Master對象,定義了一大堆變量:hadoop配置,worker超時時間、任務恢復模式、worker列表、app列表,驅動、webui等等。之後調用Master的onStart方法,正式啓動Master。
Master的onStart方法給自己發送了一個CheckForWorkerTimeOut,用於檢查是否有超時的worker,如果有則移除。
這一篇到這裏吧,以後有新的理解再更新。下一篇我們學習一下Worker的啓動過程。