spark源码学习(二)---Master源码分析(1)-master的主备切换机制
来源:互联网 发布:物业收费软件 编辑:程序博客网 时间:2024/05/19 18:43
1.spark master HA 机制概览
与hadoop一样,spark也存在单点故障问题,为此,spark的standalone模式提供了master的HA,与hadoop一样,一个是active,一个是standby状态,当active挂了,standby会顶上。spark HA的主备切换主要基于两种机制:基于文件系统和基于zk集群。前者在挂了后需要手动切换,而基于zk的HA可以自动实现切换。切换的过程如下:首先,standby的master去读取持久化(可能是磁盘或者zk)的storedAPP,storeddriver,storedworker的相关信息。读取后,如果storedAPP,storeddriver,storedworker有任何一个非空,那么就会启动master恢复机制,将持久化的APP,driver,worker信息重新注册到内存中,讲application和worker的状态修改为UNknown,然后向该APP对应的driver,worer发送自己的地址信息,driver,worer如果没有挂,那么在接收到master发送的地址后,就会返回响应消息给新的master。此时,master在接收到driver,worker的消息后,会使用completeRecory对没有响应的组件进行清理,最后调用master的schedul方法,对正在等待的driver和app调度,如启动executor。
2.下面源码分析
master位于org.apache.spark.deploy.master下,状态的恢复在beginRecovery & completeRecovery方法中:
beginRecovery 方法首先重新注册APP到内存中,注册代码如下:
private def registerApplication(app: ApplicationInfo): Unit = { val appAddress = app.driver.address if (addressToApp.contains(appAddress)) { logInfo("Attempted to re-register application at same address: " + appAddress) return } applicationMetricsSystem.registerSource(app.appSource) apps += app idToApp(app.id) = app endpointToApp(app.driver) = app addressToApp(appAddress) = app waitingApps += app }
然后将需要恢复的app和driver和worker状态都改为unknown。
private def beginRecovery(storedApps: Seq[ApplicationInfo], storedDrivers: Seq[DriverInfo], storedWorkers: Seq[WorkerInfo]) { for (app <- storedApps) { logInfo("Trying to recover app: " + app.id) try { <strong><span style="color:#33cc00;">registerApplication(app)</span> <span style="color:#33cc00;">app.state = ApplicationState.UNKNOWN</span></strong> app.driver.send(MasterChanged(self, masterWebUiUrl)) } catch { case e: Exception => logInfo("App " + app.id + " had exception on reconnect") } } for (driver <- storedDrivers) { // Here we just read in the list of drivers. Any drivers associated with now-lost workers // will be re-launched when we detect that the worker is missing. drivers += driver } for (worker <- storedWorkers) { logInfo("Trying to recover worker: " + worker.id) try { registerWorker(worker) worker.state = WorkerState.UNKNOWN worker.endpoint.send(MasterChanged(self, masterWebUiUrl)) } catch { case e: Exception => logInfo("Worker " + worker.id + " had exception on reconnect") } } }
得不到响应的driver和APP会被移除:
private def completeRecovery() { // Ensure "only-once" recovery semantics using a short synchronization period. if (state != RecoveryState.RECOVERING) { return } state = RecoveryState.COMPLETING_RECOVERY // Kill off any workers and apps that didn't respond to us.
//前面已经将新恢复worker和app的状态改成unknown,调用removeWorker和finishApplication移除worker和app<span style="white-space:pre"></span>workers.filter(_.state == WorkerState.UNKNOWN).foreach(<strong><span style="color:#009900;">removeWorker</span></strong>) apps.filter(_.state == ApplicationState.UNKNOWN).foreach(<strong><span style="color:#33cc00;">finishApplication</span></strong>) // Reschedule drivers which were not claimed by any workers drivers.filter(_.worker.isEmpty).foreach { d => logWarning(s"Driver ${d.id} was not found after master recovery") if (d.desc.supervise) { logWarning(s"Re-launching ${d.id}") <strong>relaunchDriver(d)</strong> } else { removeDriver(d.id, DriverState.ERROR, None) logWarning(s"Did not re-launch ${d.id} because it was not supervised") } } state = RecoveryState.ALIVE schedule() logInfo("Recovery complete - resuming operations!") }
下面查看removerWorker的源码:首先移除executor,再移除driver,再移除worker。
private def removeWorker(worker: WorkerInfo) { logInfo("Removing worker " + worker.id + " on " + worker.host + ":" + worker.port)//首先,将work的状态改为dead worker.setState(WorkerState.DEAD)//然后,从内存结构中移除该worker,idToWorker是一个记录worker的hashmap idToWorker -= worker.id addressToWorker -= worker.endpoint.address//遍历属于该worker的所有executor,向使用每个executor的driver发送消息,告知该executor挂了,并移除该executor for (exec <- worker.executors.values) { logInfo("Telling app of lost executor: " + exec.id) exec.application.driver.send(ExecutorUpdated( exec.id, ExecutorState.LOST, Some("worker lost"), None)) exec.application.removeExecutor(exec) }//对于driver,如果设置了对driver的监控,那么重启该driver,否则直接移除该driver for (driver <- worker.drivers.values) { if (driver.desc.<strong><span style="color:#ff0000;">supervise</span></strong>) { logInfo(s"Re-launching ${driver.id}") <span style="color:#33cc00;"><strong>relaunchDriver(driver)</strong></span> } else { logInfo(s"Not re-launching ${driver.id} because it was not supervised") removeDriver(driver.id, DriverState.ERROR, None) } }//最后移除worker persistenceEngine.removeWorker(worker) }
重新吊起driver代码:改变driver的状态,重新加入到内存中,加入后,master会重新启动该driver。其中waitingDrivers是一个ArrayBuffer:waitingDrivers = new ArrayBuffer[DriverInfo]
private def relaunchDriver(driver: DriverInfo) { driver.worker = None driver.state = DriverState.RELAUNCHING <strong>waitingDrivers </strong>+= driver schedule() }
removedriver:移除driver的代码如下:
private def removeDriver( driverId: String, finalState: DriverState, exception: Option[Exception]) {//找到该driver drivers.find(d => d.id == driverId) match { case Some(driver) => logInfo(s"Removing driver: $driverId")//从内存中移除该driver drivers -= driver if (completedDrivers.size >= RETAINED_DRIVERS) { val toRemove = math.max(RETAINED_DRIVERS / 10, 1) completedDrivers.trimStart(toRemove) }//将该driver加入到已完成的driver中 completedDrivers += driver//移除该driver的持久化信息 persistenceEngine.removeDriver(driver) driver.state = finalState driver.exception = exception//从该driver所在的worker里移除driver driver.worker.foreach(w => w.removeDriver(driver)) schedule() case None => logWarning(s"Asked to remove unknown driver: $driverId") } }}
finishApplication实际调用了removeApplication,执行过程类似:
def removeApplication(app: ApplicationInfo, state: ApplicationState.Value) { if (apps.contains(app)) { logInfo("Removing app " + app.id) apps -= app idToApp -= app.id endpointToApp -= app.driver addressToApp -= app.driver.address if (completedApps.size >= RETAINED_APPLICATIONS) { val toRemove = math.max(RETAINED_APPLICATIONS / 10, 1) completedApps.take(toRemove).foreach( a => { Option(appIdToUI.remove(a.id)).foreach { ui => webUi.detachSparkUI(ui) } applicationMetricsSystem.removeSource(a.appSource) }) completedApps.trimStart(toRemove) } completedApps += app // Remember it in our history waitingApps -= app // If application events are logged, use them to rebuild the UI asyncRebuildSparkUI(app) for (exec <- app.executors.values) { killExecutor(exec) } app.markFinished(state) if (state != ApplicationState.FINISHED) { app.driver.send(ApplicationRemoved(state.toString)) } persistenceEngine.removeApplication(app) schedule() // Tell all workers that the application has finished, so they can clean up any app state. workers.foreach { w => w.endpoint.send(ApplicationFinished(app.id)) } } }
- spark源码学习(二)---Master源码分析(1)-master的主备切换机制
- Spark源码分析之Master主备切换机制
- spark源码学习(二)---Master源码分析(2)-master内组件状态改变机制
- spark源码学习(二)---Master源码分析(3)-master对driver、executor的调度
- spark源码分析之Master源码主备切换机制分析
- Spark内核源码深度剖析:Master主备切换机制原理剖析与源码分析
- spark master注册机制和主备切换源码
- 2.Master主备机制切换源码分析
- Spark的Master分析1(主备切换机制原理分析)
- Master原理剖析与源码分析:主备切换机制原理剖析与源码分析
- 源码-spark Master(1)
- Spark源码分析-master启动
- Spark1.6源码之Master主备切换机制
- Spark源码分析之Master启动和通信机制
- Spark源码分析之Master状态改变处理机制原理
- Spark源码分析之Master注册机制原理
- Spark源码学习(二)---Master和Worker的启动以及Actor通信流程
- 源码-Spark Master(2)
- git使用过程
- javascript 快速排序法实现
- [BZOJ1176] [Balkan2007]Mokia/[BZOJ2683] 简单题
- 199Binary Tree Right Side View
- socket INADDR_ANY导致端口可以重复打开
- spark源码学习(二)---Master源码分析(1)-master的主备切换机制
- log4j【3】(简单的配置)
- NSURLSession详解
- 计数排序——CountingSort
- Androidx学习笔记(46)--- 用进度条显示下载进度
- LeetCode29. Divide Two Integers一个好答案
- lightoj1150 - Ghosts!
- 挖Linux中的古老缩略语 etc rc bin tty
- Androidx学习笔记(47)--- 借助xUtils实现下载