Master的注册机制和状态管理解密(DT大数据梦工厂)

内容:

1、Master接受Driver注册内幕;

2、Master接受Application注册内幕;

3、Master接受Worker注册内幕;

4、Master处理Drvier状态变化内幕;

5、Master处理Executor状态变化内幕;

==========Master对其它组件注册的处理============

1、Master接收注册的对象主要分为以下几个部分:Driver、Application 、Worker,需要补充说明的是:Executor不会注册给Master,Executor是注册给SchedulerBackend的;

override def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = {
  case RegisterWorker(
      idworkerHostworkerPortworkerRefcoresmemoryworkerUiPortpublicAddress) => {
    logInfo("Registering worker %s:%d with %d cores, %s RAM".format(
      workerHostworkerPortcoresUtils.megabytesToString(memory)))
    if (state == RecoveryState.STANDBY) {
      context.reply(MasterInStandby)
    } else if (idToWorker.contains(id)) {
      context.reply(RegisterWorkerFailed("Duplicate worker ID"))
    } else {
      val worker = new WorkerInfo(idworkerHostworkerPortcoresmemory,
        workerRefworkerUiPortpublicAddress)
      if (registerWorker(worker)) {
        persistenceEngine.addWorker(worker)
        context.reply(RegisteredWorker(selfmasterWebUiUrl))
        schedule()
      } else {
        val workerAddress = worker.endpoint.address
        logWarning("Worker registration failed. Attempted to re-register worker at same " +
          "address: " + workerAddress)
        context.reply(RegisterWorkerFailed("Attempted to re-register worker at same address: "
          + workerAddress))
      }
    }
  }

case RegisterApplication(descriptiondriver) => {
  // TODO Prevent repeated registrations from some driver
  if (state == RecoveryState.STANDBY{
    // ignore, don't send response
  } else {
    logInfo("Registering app " + description.name)
    val app = createApplication(descriptiondriver)
    registerApplication(app)
    logInfo("Registered app " + description.name + " with ID " + app.id)
    persistenceEngine.addApplication(app)
    driver.send(RegisteredApplication(app.idself))
    schedule()
  }
}

case RequestSubmitDriver(description) => {
  if (state != RecoveryState.ALIVE) {
    val msg = s"${Utils.BACKUP_STANDALONE_MASTER_PREFIX}$state. " +
      "Can only accept driver submissions in ALIVE state."
    context.reply(SubmitDriverResponse(selffalseNonemsg))
  } else {
    logInfo("Driver submitted " + description.command.mainClass)
    val driver = createDriver(description)
    persistenceEngine.addDriver(driver)
    waitingDrivers += driver
    drivers.add(driver)
    schedule()

    // TODO: It might be good to instead have the submission client poll the master to determine
    //       the current status of the driver. For now it's simply "fire and forget".

    context.reply(SubmitDriverResponse(selftrueSome(driver.id),
      s"Driver successfully submitted as ${driver.id}"))
  }
}

==========Master接受Worker注册内幕 ============

1、Worker是在启动之后主动向Master注册的,好处:如果生产环境有500台机器集群,再加100台新Worker机器到已经在运行的Spark集群上,此时不需要重新启动Spark集群就能够使用新加入的Worker,以提升处理能力;

override def onStart() {
  assert(!registered)
  logInfo("Starting Spark worker %s:%d with %d cores, %s RAM".format(
    hostportcoresUtils.megabytesToString(memory)))
  logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")
  logInfo("Spark home: " sparkHome)
  createWorkDir()
  shuffleService.startIfEnabled()
  webUi new WorkerWebUI(thisworkDirwebUiPort)
  webUi.bind()
  registerWithMaster()

  metricsSystem.registerSource(workerSource)
  metricsSystem.start()
  // Attach the worker metrics servlet handler to the web ui after the metrics system is started.
  metricsSystem.getServletHandlers.foreach(webUi.attachHandler)
}

对应的就是Master中的registerWorker里面,源码中看到STANDBY机器并不注册

2、Master在接收到Worker的注册的请求后,首先会判断一下当前的Master是否是standby模式,如果是的话就不处理,然后会判断当前Master的内存数据结构idToWorker中是否已经有该Worker的注册信息,如果有的话,此时不会重复注册

3、Master如果决定接收注册的Worker,首先会创建WorkerInfo对象来保存注册Worker的信息,

 val worker = new WorkerInfo(idworkerHostworkerPortcoresmemory,
    workerRefworkerUiPortpublicAddress)
  if (registerWorker(worker)) {
    persistenceEngine.addWorker(worker)
    context.reply(RegisteredWorker(selfmasterWebUiUrl))
    schedule()
  } else {
    val workerAddress = worker.endpoint.address
    logWarning("Worker registration failed. Attempted to re-register worker at same " +
      "address: " + workerAddress)
    context.reply(RegisterWorkerFailed("Attempted to re-register worker at same address: "
      + workerAddress))
  }
}

通过filter方法过滤状态为DEAD的worker,还有UNKOWN(集群切换的时候,会产生UNKOWN 的状态,具体见29讲)会调用removeWorker方法清理掉其曾经的Worker信息并替换为新的Worker信息(并清理掉Worker下的Executors和Drviers)

private def registerWorker(worker: WorkerInfo): Boolean = {
  // There may be one or more refs to dead workers on this same node (w/ different ID's),
  // remove them.
  workers.filter { w =>
    (w.host == worker.host && w.port == worker.port) && (w.state == WorkerState.DEAD)
  }.foreach { w =>
    workers -= w
  }

  val workerAddress = worker.endpoint.address
  if (addressToWorker.contains(workerAddress)) {
    val oldWorker = addressToWorker(workerAddress)
    if (oldWorker.state == WorkerState.UNKNOWN) {
      // A worker registering from UNKNOWN implies that the worker was restarted during recovery.
      // The old worker must thus be dead, so we will remove it and accept the new worker.
      removeWorker(oldWorker)
    } else {
      logInfo("Attempted to re-register worker at same address: " + workerAddress)
      return false
    }
  }

  workers += worker
  idToWorker(worker.id) = worker
  addressToWorker(workerAddress) = worker
  true
}

4、把注册的Worker加入到Master的内存的数据结构中;

5、registerWorker之后就是要persisteEngine,要持久化到持久化引擎中(例如Zookeeper),具体到哪个引擎,可以看29讲;

6、然后回复,回复之后干了一件大事,Schedule,调度(后面花大篇幅来讲解);

==========Master接受Driver注册内幕============

1、Master会把Drvier的信息放入内存缓存中;

2、加入等待调度的队列;

3、用持久化引擎把信息持久化;

4、回复,调度;

==========Master接受Application注册内幕============

注意:注册的时候是先注册Drvier,然后再注册Application;

在Driver启动后,SparkContext的初始化,进而导致SparkDeploySchedulerBackend的产生,其内部AppClient,AppClient的内部有ClientEndpoint来发送RegisterApplication信息给Master:

1、将Application的信息放入内存缓存中;

2、将Application加入等待调度的Application队列中;

3、用持久化引擎把信息持久化;

4、回复,调度;

spacer.gif

==========Master处理Drvier状态变化内幕============

只要是ERROR、FINISHED、KILLED、FAILED,都remove了

case DriverStateChanged(driverIdstateexception) => {
  state match {
    case DriverState.ERROR | DriverState.FINISHED | DriverState.KILLED | DriverState.FAILED =>
      removeDriver(driverIdstateexception)
    case _ =>
      throw new Exception(s"Received unexpected state update for driver $driverId$state")
  }
}

==========Master处理Executor状态变化内幕============

Executor挂掉的时候,系统尝试一定次数的重启(最多重试10次)

case ExecutorStateChanged(appIdexecIdstatemessageexitStatus) => {
  val execOption = idToApp.get(appId).flatMap(app => app.executors.get(execId))
  execOption match {
    case Some(exec) => {
      val appInfo = idToApp(appId)
      val oldState = exec.state
      exec.state = state

      if (state == ExecutorState.RUNNING) {
        assert(oldState == ExecutorState.LAUNCHING,
          s"executor $execId state transfer from $oldState to RUNNING is illegal")
        appInfo.resetRetryCount()
      }

      exec.application.driver.send(ExecutorUpdated(execIdstatemessageexitStatus))

      if (ExecutorState.isFinished(state)) {
        // Remove this executor from the worker and app
        logInfo(s"Removing executor ${exec.fullId} because it is $state")
        // If an application has already finished, preserve its
        // state to display its information properly on the UI
        if (!appInfo.isFinished) {
          appInfo.removeExecutor(exec)
        }
        exec.worker.removeExecutor(exec)

        val normalExit = exitStatus == Some(0)
        // Only retry certain number of times so we don't go into an infinite loop.
        if (!normalExit) {
          if (appInfo.incrementRetryCount() < ApplicationState.MAX_NUM_RETRY) {
            schedule()
          } else {
            val execs = appInfo.executors.values
            if (!execs.exists(_.state == ExecutorState.RUNNING)) {
              logError(s"Application ${appInfo.desc.name} with ID ${appInfo.id} failed " +
                s"${appInfo.retryCount} times; removing it")
              removeApplication(appInfoApplicationState.FAILED)
            }
          }
        }
      }
    }
    case None =>
      logWarning(s"Got status update for unknown executor $appId/$execId")
  }
}

王家林老师名片:

中国Spark第一人

新浪微博:http://weibo.com/ilovepains

微信公众号:DT_Spark

博客:http://blog.sina.com.cn/ilovepains

手机:18610086859

QQ:1740415547

邮箱:[email protected]


本文出自 “一枝花傲寒” 博客,谢绝转载!

你可能感兴趣的:(Master的注册机制和状态管理解密(DT大数据梦工厂))