【源码共读】YARN HA方案ResourceManager的选举流程

1,785 阅读4分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

YARN HA方案 EmbeddedElector +StandByTransitionRunnable

执行原理 : YARN HA ResourceManager的选举,首先由EmbeddedElector来做最多3次的同步尝试选举,如果没有选举成功,则交给StandByTransitionRunnable线程来维护选举

首先来研究YARN HA的实现方案:

先来简要的说明上图:

  • 灰色部分代表的是zookeeper中的部分,/是根目录,在选举方案中根目录下面有一个znode = yarn-leader-election,在该znode下面还有一个znode代表的clusterID
  • 当开启了HA模式的时候,集群启动,两个ResourceManager就会争相竞选,的在clusterID的znode下面创建ActiveStandbyElectorLock锁节点,两个ResourceManager谁首先创建了该节点谁就是active竞选成功,然后将自己的 ResourceManagerID 写到此节点中,并且创建另一个znode(ActiveBreadCrumb)来存储active节点的一些信息
  • 另一个竞选失败的RM将会监听ActiveStandbyElectorLock,当集群中active发展故障下线的时候,ActiveStandbyElectorLock锁节点就会被删除,然后standby节点就会开始争抢创建锁节点

下面从源码中来印证,开启了HA,集群启动时候,会走ResourceManager.serviceInit() 方法的elector服务来进行竞选,也就是 ActiveStandbyElectorBasedElectorService类,其serviceInit()方法之前的文章(Yarn ResourceManager启动流程源码解读)已经叙述,下面直接从serviceStart()方法入手:

进行选举的入口

localActiveNodeInfo = clusterid + rmid 对应zookeeper上面znode所存储的数据的内容

protected void serviceStart() throws Exception {
    elector.joinElection(localActiveNodeInfo);
    super.serviceStart();
  }

下面详看 joinElection(localActiveNodeInfo) 方法

首先是拷贝数据,,然后转到另一个方法joinElectionInternal()

appData = new byte[data.length];
joinElectionInternal()

详看 joinElectionInternal() 方法

确保Client的存在

if (zkClient == null) {
      if (!reEstablishSession()) {
        fatalError("Failed to reEstablish connection with ZooKeeper");
        return;
      }
}

通过异步的方式创建分布式锁节点

createLockNodeAsync(){
    //创建一个选举的临时节点 EPHEMERAL
    zkClient.create(zkLockFilePath, appData, zkAcl, CreateMode.EPHEMERAL,this, zkClient);
}

上述代码的cb为一个回调函数(第五个参数),当zookeeper创建节点后转到回调函数进行相关的处理

回调函数cb为:callback = ActiveStandbyElector.processResult()

下面详细的查看函数processResult()

  1. 当创建锁节点成功的时候,当前的节点就会变为active状态
processResult(){
    if (isSuccess(code)) {
      // 当成功创建了znode的时候(ActiveStandbyElectorLock),状态就会编程active leader ,开启监控
      //todo 成为active
      if (becomeActive()) {
        monitorActiveStatus();
      } else {
        reJoinElectionAfterFailureToBecomeActive();
      }
      return;
    }
}

becomeActive() 方法的具体内容为:

ActiveStandbyElector.becomeActive(){
    //如果已经成为了active节点 说明该做的事情已经做完了 直接返回
    if (state == State.ACTIVE) {
      // already active
      return true;
    }
    
    //当没有成为active节点的时候
    //创建 ActiveBreadCrumb znode节点
    writeBreadCrumbNode(oldBreadcrumbStat);
    //进行状态的切换
    appClient.becomeActive();
    //对state成员变量赋值为ACTIVE
    state = State.ACTIVE;
}

跳转到 ActiveStandbyElectorBasedElectorService.becomeActive() 方法

将active节点才能启动的Service都进行启动

rm.getRMContext().getRMAdminService().transitionToActive(req){
    rm.transitionToActive(){
        public Void run() throws Exception {
          //todo 启动相关的服务
          startActiveServices(){
              if (activeServices != null) {
                clusterTimeStamp = System.currentTimeMillis();
                activeServices.start();
              }
          }
      }
   }
}

走到此处的时候,就跳转到另一个Service activeServices ,直接看该Service的serviceStart()方法即可


  1. 当没有成功创建的时候 isSuccess(code) = false

此时有两种情况:

1. 节点已经存在,别人已经创建成功
2. 节点不存在,别人没有创建成功,但是我也没有创建成功

第一种,别人已经创建成功锁节点,isNodeExists(code) = true ,此时当前节点直接成为standby状态

if (isNodeExists(code)) {
      if (createRetryCount == 0) {
        becomeStandby();
      }
      monitorActiveStatus();
      return;
    }

becomeStandby() 方法的具体内容为:

ActiveStandbyElectorBasedElectorService.becomeStandby(){
    rm.getRMContext().getRMAdminService().transitionToStandby(req){
        rm.transitionToStandby(true){
            //todo 获取状态
            HAServiceState state = rmContext.getHAServiceState();
            //todo 将状态设置为standby
            rmContext.setHAServiceState(HAServiceProtocol.HAServiceState.STANDBY);
            //如果获取到之前的状态为actice 则关闭那些active的服务
            if (state == HAServiceProtocol.HAServiceState.ACTIVE) {
            //todo 关闭active service
            stopActiveServices();
            //todo initialize = true
            reinitialize(initialize){
                ClusterMetrics.destroy();
                QueueMetrics.clearQueueMetrics();
                getResourceScheduler().resetSchedulerMetrics();
                //todo true
                if (initialize) {
                    //重置 上下文环境
                    resetRMContext();
                    //创建并且初始化新的activeService
                    createAndInitActiveServices(true);
    }
            }
        }
    }
}

第二种,没有成功创建锁节点并且锁节点到目前为止还没有被创建成功

  • 当重试的次数小于最大的重试次数的时候就重新进行竞选,createLockNodeAsync() 就又返回到创建锁节点的地方
if (createRetryCount < maxRetryNum) {
        ++createRetryCount;
        createLockNodeAsync();
        return;
      }
  • 当重试的次数大于了 maxRetryNum 的时候

最后交给线程StandByTransitionRunnable来选举 使用一个守护线程来进行选举

不要阻塞ResourceManager主线程其它服务的启动

fatalError(errorMessage){
    appClient.notifyFatalError{
        notifyFatalError{
            // RMFatalEventDispatcher.handle
            rm.getRMContext().getDispatcher().getEventHandler().handle(new RMFatalEvent(RMFatalEventType.EMBEDDED_ELECTOR_FAILED,errorMessage));
        }
    }
}

上面的方法用到了AsyncDispatcher,跳转到 RMFatalEventDispatcher.handle()

RMFatalEventDispatcher.handle(){
    //启动线程 StandByTransitionRunnable
    handleTransitionToStandByInNewThread(){
        standByTransitionThread.start(){
            public void run() {
                //获得选举的选举器 elector
                EmbeddedElector elector = rmContext.getLeaderElectorService();
                //重新加入选举
                elector.rejoinElection(){
                    //退出可能存在的选举
                    elector.quitElection(false);
                    //重新加入选举
                    elector.joinElection(localActiveNodeInfo){
                        //拷贝数据
                        appData = new byte[data.length];
                        //选举
                        joinElectionInternal();
                    }
                }
            }
        }
    }
}

此处就转到了原来的方法 joinElectionInternal() 重新进行选举,之后的流程就完全相同了

至此,YARN HA 的ResourceManager的选举就全部结束


本人在学习的路上,上述文章如有错误还请指教批评。