美文网首页IT@程序员猿媛HADOOP从入门到放弃
HDFS中ZKFailoverController的原理探究

HDFS中ZKFailoverController的原理探究

作者: 叫我不矜持 | 来源:发表于2019-05-07 07:53 被阅读4次

    自动化failover的引入

    HDFS中自动化的failover故障转移需要增加两个新的组件:一个是Zookeeper quorum(仲裁),另一个是ZKFailoverController进程(简称ZKFC)。

    Apache Zookeeper是一个高可用的服务,对于小规模数据协调,通知客户端数据变化,监控客户端失败。自动failover的实现是基于ZK以下的作用:

    • Failure detection
      集群中的每个NameNode机器在ZK上保持持久化会话。如果机器崩溃,ZK会话过期,通知其它NameNode有一个failover将被触发。

    • Active NameNode election
      ZK提供一个简单机制,选举出唯一的一个节点作为active。如果当前的active NameNode崩溃,另一个节点可能在ZK持有特定的互斥型锁,表名它将成为下一个active。


    ZKFC是一个ZK客户端,也监控和管理NameNode的状态。NameNode运行的所在的每个机器也要运行一个ZKFC。

    ZKFC负责:

    • 健康监测
      ZKFC定期使用健康检查命令调用其本地NameNode。只要NameNode以健康的状态及时响应,ZKFC就会认为节点是健康的。
      如果节点已崩溃、冻结或以其他方式进入不健康状态,则健康监视器将将其标记为不健康。

    • ZooKeeper会话管理
      当本地NameNode健康时,ZKFC在ZooKeeper中举行一个开放的会话。
      如果本地NameNode是活动的,它也持有一个特殊的“锁”。此锁使用ZooKeeptor对“临时”节点的支持;如果会话过期,则将自动删除锁节点。

    • 基于ZooKeeper的选举
      如果本地NameNode是健康的,而ZKFC认为目前没有其他节点持有锁,
      它本身就会尝试获取锁。如果它成功了,那么它已经“赢得了选举”,并负责运行故障转移以使其本地NameNode活动。故障转移过程类似于上面描述的手动故障转移:首先,如果需要,对前一个活动进行隔离,然后本地NameNode转换到活动状态。


    问题:

    • 一般导致NameNode切换的原因
    • ZKFC的作用是什么?如何判断一个NN是否健康
    • NameNode HA是如何实现的?
    • NameNode因为断电导致不能切换的原理,怎样进行恢复

    一般导致NameNode切换的原因

    随着集群规模的变大和任务量变多,NameNode的压力会越来越大,一些默认参数已经不能满足集群的日常需求,除此之外,异常的Job在短时间内创建和删除大量文件,引起NN节点频繁更新内存的数据结构从而导致RPC的处理时间变长,CallQueue里面的RpcCall堆积,甚至严重的情况下打满CallQueue,导致NameNode响应变慢,甚至无响应,ZKFC的HealthMonitor监控自己的NN异常时,则会断开与ZooKeeper的链接,从而释放锁,另外一个NN上的ZKFC进行抢锁进行Standby到Active状态的切换。这是一般引起的切换的流程。

    当然,如果你是手动去切换这也是可以的,当Active主机出现异常时,有时候则需要在必要的时间内进行切换。

    ZKFC的作用是什么?如何判断一个NN是否健康

    在正常的情况下,ZKFC的HealthMonitor主要是监控NameNode主机上的磁盘还是否可用(空间),我们都知道,NameNode负责维护集群上的元数据信息,当磁盘不可用的时候,NN就该进行切换了。

     /**
       * Return true if disk space is available on at least one of the configured
       * redundant volumes, and all of the configured required volumes.
       * 
       * @return True if the configured amount of disk space is available on at
       *         least one redundant volume and all of the required volumes, false
       *         otherwise.
       */
      public boolean hasAvailableDiskSpace() {
        return NameNodeResourcePolicy.areResourcesAvailable(volumes.values(),
            minimumRedundantVolumes);
      }
    
    

    除了可用状态(SERVICE_HEALTHY)之外,还有SERVICE_UNHEALTHY(磁盘空间不可用),SERVICE_NOT_RESPONDING(其他的一些情况)状态,在这两个状态中,它都认为NN是不健康的。

    NameNode HA是如何实现的?

    我们前面说到,ZKFC是如何判断NN是否健康,接下来当NN处于非健康状态时,NameNode是如何进行切换的呢?

    image

    在ZKFailoverController这个类中,实行了两个重要的Callbacks函数,一个叫ElectorCallbacks,另一个叫HealthCallbacks,顾名思义就是选举和健康检查用的回调函数,其中还有两个重要的组成部分elector(ActiveStandbyElector)healthMonitor(HealthMonitor),总体的就如上图所示。

    ElectorCallbacks:

    /**
       * Callbacks from elector
       */
      class ElectorCallbacks implements ActiveStandbyElectorCallback {
        @Override
        public void becomeActive() throws ServiceFailedException {
          ZKFailoverController.this.becomeActive();
        }
    
        @Override
        public void becomeStandby() {
          ZKFailoverController.this.becomeStandby();
        }
    ...
    }
    
    

    HealthCallbacks:

     /**
       * Callbacks from HealthMonitor
       */
      class HealthCallbacks implements HealthMonitor.Callback {
        @Override
        public void enteredState(HealthMonitor.State newState) {
          setLastHealthState(newState);
          recheckElectability();
        }
      }
    
    

    对于HealthMonitor来说,在ZKFC进程启动的时候,就已经将HealthCallbacks注册进去了,HealthMonitor都会定期的检查NameNode是否健康,我们可以通过监控ha.health-monitor.check-interval.ms去设置监控的间隔时间和通过参数ha.health-monitor.rpc-timeout.ms设置timeout时间,当集群变大的时候,需要适当的设置改值,让ZKFC的HealthMonitor没那么“敏感”

    ZKFC通过RPC调用监控NN进程,当出现异常时,则进入不同的处理逻辑,以下是简化的代码:

     private void doHealthChecks() throws InterruptedException {
        while (shouldRun) {     
          try {
            status = proxy.getServiceStatus();
            proxy.monitorHealth();
            healthy = true;
          } catch (HealthCheckFailedException e) {
           ...
            enterState(State.SERVICE_UNHEALTHY);
          } catch (Throwable t) {
           ...
            enterState(State.SERVICE_NOT_RESPONDING);
            Thread.sleep(sleepAfterDisconnectMillis);
            return;
          }
          ...
    }
    
    

    回调函数就是这么起作用啦,那么回调函数做了什么呢?总的来说,如果NN健康(SERVICE_HEALTHY)就加入选举,如果不健康就退出选举(SERVICE_UNHEALTHYSERVICE_NOT_RESPONDING

     case SERVICE_UNHEALTHY:
            case SERVICE_NOT_RESPONDING:
              LOG.info("Quitting master election for " + localTarget +
                  " and marking that fencing is necessary");
              elector.quitElection(true);
              break;
    
    

    说到退出选举就关系到elector(ActiveStandbyElector)了,true代表如果NN从Actice变为Standby出现异常是要去fence的,这就是为啥NN会挂掉的原因之一

    如何退出选举?就是close zkClient的链接,让ZooKeeper上面的维持的选举锁消失

    void terminateConnection() {
        if (zkClient == null) {
          return;
        }
        LOG.debug("Terminating ZK connection for " + this);
        ZooKeeper tempZk = zkClient;
        ...
        try {
          tempZk.close();
        } catch(InterruptedException e) {
          LOG.warn(e);
        }
       ...
      }
    
    

    对于ActiveStandbyElector来说,他有个WatcherWithClientRef类专门用来监听ZooKeeper上的的znode的事件变化,当事件变化时,就会调用ActiveStandbyElector的processWatchEvent的方法

    watcher = new WatcherWithClientRef();
    ZooKeeper zk = new ZooKeeper(zkHostPort, zkSessionTimeout, watcher);
    
    

    /**
       * Watcher implementation which keeps a reference around to the
       * original ZK connection, and passes it back along with any
       * events.
       */
      private final class WatcherWithClientRef implements Watcher {
    ...
        @Override
            public void process(WatchedEvent event) {
              hasReceivedEvent.countDown();
              try {
                hasSetZooKeeper.await(zkSessionTimeout, TimeUnit.MILLISECONDS);
                ActiveStandbyElector.this.processWatchEvent(
                    zk, event);
              } catch (Throwable t) {
                fatalError(
                    "Failed to process watcher event " + event + ": " +
                    StringUtils.stringifyException(t));
              }
            }
    ...
    }
    
    

    在ActiveStandbyElector的processWatchEvent方法中,处理来自不同事件的逻辑重新加入选举或者继续监控znode的变化,当另外一个ZKFC监控到事件变化得时候,就去抢锁,抢锁实质上就是创建znode的过程,而且创建的是CreateMode.EPHEMERAL类型的,所以,当HealthMonitor监控到NN不健康时,就会断开连接,节点就会消失,watcher就会监控到NodeDeleted事件,进行创建节点。

     switch (eventType) {
          case NodeDeleted:
            if (state == State.ACTIVE) {
              enterNeutralMode();
            }
            joinElectionInternal();
            break;
          case NodeDataChanged:
            monitorActiveStatus();
            break;
    
    

    又因为ActiveStandbyElector实现了StatCallback接口,当节点创建成功时,就会回调processResult方法看是否创建成功,如果创建成功则去检查zkBreadCrumbPath是否存在之前的Active节点,如果存在,则调用RPC让其变为Standby,看能否转变成功,否则则SSH过去fence掉NN进程。,保持Active节点只有一个,并且恢复正常服务

    NameNode因为断电导致不能切换的原理,怎样进行恢复

    ActiveNN断电,网络异常,负载过高或者机器出现异常无法连接,Standby NN无法转化为Active,使得HA集群无法对外服务,原因是Active NN节点在断电和不能服务的情况下,zknode上保存着ActiveBreadCrumb, ActiveStandbyElectorLock两个Active NN的信息,ActiveStandbyElectorLock由于Active NN出现异常断开,Standby NN去抢锁的时候就会去检查ActiveBreadCrumb是否有上一次的Active NN节点,如果有,就会就会尝试让Active NN变为Standby NN,自己转化为Active NN,但是由于调用出现异常,所以会采用ssh的方式去Fence之前的Active NN,因为机器始终连接不上,所以无法确保old active NN变为Standby NN,自己也无法变为Active NN,所以还是保持Standby状态,避免出现脑裂问题。

    解决方案是确定Active关机的情况下重新hdfs zkfc -formatZK就可以了。

    总 结

    NN GC或者在压力大的情况下可以调整GC算法和增加NameNode节点的线程数,加快NN对请求的处理速度,也可以分离节点的端口dfs.namenode.rpc-address.ns1.nn2dfs.namenode.servicerpc-address.ns1.nn2分离client和datanode节点等服务类型的请求,进行分担压力,也可以适当的调整ZKFC的监控timeout的时间等等

    转载自:初始ZKFC的原理

    相关文章

      网友评论

        本文标题:HDFS中ZKFailoverController的原理探究

        本文链接:https://www.haomeiwen.com/subject/zrejoqtx.html