【hadoop二次开发】根据汇报的数据块判断是否可以离开安全模式

008-hadoop二次开发-NameNode启动流程

在源码文件FSNamesystem.java执行完nnResourceChecker = new NameNodeResourceChecker(conf);
立马执行checkAvailableResources(),检查可用资源是否足够:如果不够,日志打印警告信息,然后进入安全模式。
然后

  /**
   * 磁盘资源不足的情况下,任何对元数据修改所产生的日志都无法确保能够写入到磁盘,
   * 即新产生的edits log和fsimage都无法确保写入磁盘。所以要进入安全模式,
   * 来禁止元数据的变动以避免往磁盘写入新的日志数据
   * */
  assert safeMode != null && !isPopulatingReplQueues();
  /**
   * Check if replication queues are to be populated
   * @return true when node is HAState.Active and not in the very first safemode
   */
  /**这个方法是用来检查副本队列是否应该被同步/复制*/
  @Override
  public boolean isPopulatingReplQueues() {
    if (!shouldPopulateReplQueues()) {
      return false;
    }
    return initializedReplQueues;
  }

设置数据块汇报

//处于一个等待汇报blocks的状态
prog.setTotal(Phase.SAFEMODE, STEP_AWAITING_REPORTED_BLOCKS, getCompleteBlocksTotal());
  /**
   * NN端的block有四种状态:
   * 1、UnderConstruction(正在被写入的状态)
   * 2、UnderRecovery(正在被恢复的块)
   * 3、Committed(已经确定好它的字节大小与generation stamp值(可理解为版本号))
   * 4、Complete(写入执行操作结束状态)
   * 启动的时候 ,namenode认为只有block状态为Complete,才会被读取
   *
   * 获取系统中的COMPLETE块总数。
   * 对于安全模式,仅计算完整块。
   */
  private long getCompleteBlocksTotal() {
    // Calculate number of blocks under construction
    long numUCBlocks = 0;
    readLock();
    //获取所有正在构建的block块
    //leaseManager 文件租约(为每一个客户端和文件构建关联)
    numUCBlocks = leaseManager.getNumUnderConstructionBlocks();
    try {
      return getBlocksTotal() - numUCBlocks;
    } finally {
      readUnlock();
    }
  }

设置所有的block,用于后面判断是否进入安全模式setBlockTotal(),

  /**
   * Set the total number of blocks in the system. 
   */
  public void setBlockTotal() {
    // safeMode is volatile, and may be set to null at any time
    SafeModeInfo safeMode = this.safeMode;
    if (safeMode == null)
      return;
    //TODO 设置安全模式
    //getCompleteBlocksTotal 获取所有正常使用的block个数
    safeMode.setBlockTotal((int)getCompleteBlocksTotal());
  }

通过SafeModeInfo safeMode = this.safeMode;拿到状态信息,

/** Time when threshold was reached.
 * <br> -1 safe mode is off
 * <br> 0 safe mode is on, and threshold is not reached yet
 * <br> >0 safe mode is on, but we are in extension period 
 */
private long reached = -1;  
/**
 * 1、得到满足安全模式阈值条件所需的块数
 * 2、填充复制队列之前所需的块数
 * 3、检查是否需要进入安全模式
 */
private synchronized void setBlockTotal(int total) {
  this.blockTotal = total;//汇报过来的blocks(complete状态)个数
  //满足安全模式阈值条件所需的块数  blockTotal * 0.999f  1000 * 0.999 = 999
  this.blockThreshold = (int) (blockTotal * threshold);
  //填充复制队列之前所需的块数 blockTotal * 0.999f
  this.blockReplQueueThreshold = (int) (blockTotal * replQueueThreshold);
  if (haEnabled) {
    // After we initialize the block count, any further namespace
    // modifications done while in safe mode need to keep track
    // of the number of total blocks in the system.
    this.shouldIncrementallyTrackBlocks = true;
  }
  /**
    * blockSafe是datanode向namenode进行汇报的块个数,通过incrementSafeBlockCount方法,不断的叠加起来的
    * 通过decrementSafeBlockCount,当datanode向namenode汇报删除数据块的时候,此处就对blockSafe减小
     * */
    if(blockSafe < 0)
      this.blockSafe = 0;

    checkMode();
  }
/**
 * 用于检查安全模式的状态:
 * 1、判断阈值系数是否满足进入安全模式:needEnter
 * 对于离开安全模式,有两个条件判断:
 * 1、判断系数是否满足离开安全模式
 * 2、启动SafeModeMonitor线程,每隔1秒去查看下,是否可以退出安全模式
 */
private void checkMode() {

  /**assert 如果<boolean表达式>为true,则程序继续执行。
   如果为false,则程序抛出AssertionError,并终止执行。*/
  assert hasWriteLock();//安全模式下,需要写锁,禁止写入

  //如果当前节点已经是active状态,则不需要在检查了,直接返回
  if (inTransitionToActive()) {
    return;
  }

  //TODO 判断是否进入安全模式
  if (smmthread == null && needEnter()) {
    //进入安全模式
    enter();
    /**
     * canInitializeReplQueues:判断namenode已经接收到的blockSafe块数量是否达到了
     * 恢复复制和删除数据块数据块数量
     * */
    if (canInitializeReplQueues() && !isPopulatingReplQueues()
        && !haEnabled) {
      initializeReplQueues();
    }
    reportStatus("STATE* Safe mode ON.", false);
    return;
  }
  /**
   * extension处于安全模式的时间(满足了退出安全模式条件 ,此时还是处于安全模式的时间)
   * threshold安全系数
   * isOn 当前的状态(reached)是否小于0
   * */
  if (!isOn() || extension <= 0 || threshold <= 0) {  // don't need to wait
    this.leave(); // 退出安全模式
    return;
  }
  if (reached > 0) {  // threshold has already been reached before
    reportStatus("STATE* Safe mode ON.", false);
    return;
  }
  /**TODO 启动SafeModeMonitor线程,每隔1秒去查看下,是否可以退出安全模式*/
  reached = monotonicNow();
  reachedTimestamp = now();
  if (smmthread == null) {
    smmthread = new Daemon(new SafeModeMonitor());
    smmthread.start();
    reportStatus("STATE* Safe mode extension entered.", true);
  }

  // check if we are ready to initialize replication queues
  if (canInitializeReplQueues() && !isPopulatingReplQueues() && !haEnabled) {
    initializeReplQueues();
  }
}

是否进入安全模式的enter方法

/**
 * Enter safe mode.
 */
private void enter() {
  /**
   *  reached
   * <br> -1 退出安全模式
   * <br> 0 进入安全模式
   * */
  this.reached = 0;
  this.reachedTimestamp = 0;
}

怎么进入安全模式?
首先守护线程smmthread为空,并且needEnter()为真,

/** 
 * There is no need to enter safe mode 
 * if DFS is empty or {@link #threshold} == 0
 */
/**
 * 进入安全模式有3个条件,任何一个满足,都会进入安全模式
 * 条件1:threshold != 0 && blockSafe < blockThreshold)
 *      this.blockThreshold = (int) (blockTotal * threshold);
 *      也就是说:如果有1000个block,那么阈值blockThreshold=999
 *      但是如果集群启动,datanode汇报过来的累计的块数小于<999,那么就会进入安全模式
 * 条件2:datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold
 *       启动集群后,存活的datanode个数小于datanodeThreshold(默认0),则进入安全模式
 *
 * 条件3:!nameNodeHasResourcesAvailable()
 *       检查磁盘,是否大于100M
 *
 * */
private boolean needEnter() {
  //假如1000个数据块 , blockSafe < 999
  return (threshold != 0 && blockSafe < blockThreshold) ||
    (datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold) ||
    (!nameNodeHasResourcesAvailable());
}

是否离开安全模式中的isOn方法

/**
 * Check if safe mode is on.
 * @return true if in safe mode
 */
private synchronized boolean isOn() {
  doConsistencyCheck();
  //安全模式化 reached = 0
  //非  reached = -1
  return this.reached >= 0;
}

接下来创建守护线程
进入SafeModeMonitor线程,实现了Runnable接口,在run方法中每个1s钟循环检查是否要退出安全模式,整体加了写锁,最后释放写锁。

    @Override
    public void run() {
      while (fsRunning) {
        writeLock();
        try {
          if (safeMode == null) { // Not in safe mode.
            break;
          }
          //TODO 判断是否满足退出安全模式
          if (safeMode.canLeave()) {
            // Leave safe mode.
            safeMode.leave();//方法中将 reached = -1;
            smmthread = null;//将守护线程置空
            break;
          }
        } finally {
          writeUnlock();
        }

        try {
          Thread.sleep(recheckInterval);//TODO 每隔1s检查一遍是否满足条件
        } catch (InterruptedException ie) {
          // Ignored
        }
      }
      if (!fsRunning) {
        LOG.info("NameNode is being shutdown, exit SafeModeMonitor thread");
      }
    }
  }

若退出安全模式,需满足safeMode.canLeave()为true,

/**
 * 通过3个条件来判断是否可以离开安全模式:
 * 1、reached是否为-1
 * 2、在满足最小副本条件之后,namenode还需要处于安全模式的时间(30s)
 * 3、needEnter里面的3个条件
 * */
private synchronized boolean canLeave() {
  //reached == -1是离开safemode
  if (reached == 0) {
    return false;
  }

  //extension 默认30s,也就是满足最低副本系数之后,离开安全模式的时间,这个时间用于等待剩余数据节点的数据块上报
  //这里的reached是来自于创建守护线程前reached = monotonicNow();记录一个当前时间
  //此处的monotonicNow()又拿到一个当前时间
  if (monotonicNow() - reached < extension) {
    reportStatus("STATE* Safe mode ON, in safe mode extension.", false);
    return false;
  }

  if (needEnter()) {
    reportStatus("STATE* Safe mode ON, thresholds not met.", false);
    return false;
  }

  return true;
}

monotonicNow()保障时间的准确性

  public static long monotonicNow() {
    final long NANOSECONDS_PER_MILLISECOND = 1000000;

    return System.nanoTime() / NANOSECONDS_PER_MILLISECOND;
  }

最后

//TODO 启动BlockManager里面关于block副本处理的后台线程
//激活BlockManager
blockManager.activate(conf);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值