HDFS安全模式详解

什么是安全模式

安全模式是HDFS所处的一种特殊状态,在这种状态下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。在NameNode主节点启动时,HDFS首先进入安全模式,DataNode在启动的时候会向namenode汇报可用的block等状态,当整个系统达到安全标准时,HDFS自动离开安全模式。如果HDFS出于安全模式下,则文件block不能进行任何的副本复制操作,因此达到最小的副本数量要求是基于datanode启动时的状态来判定的,启动时不会再做任何复制(从而达到最小副本数量要求)

下面是namenode的一个日志片段:

这里写图片描述

安全模式相关的配置

系统什么时候才离开安全模式,需要满足哪些条件?当收到来自datanode的状态报告后,namenode根据配置,确定 1)可用的block占总数的比例、2)可用的数据节点数量符合要求之后,离开安全模式。如果有必要,也可以通过命令强制离开安全模式。与安全模式相关的主要配置在hdfs-site.xml文件中,主要有下面几个属性

  • dfs.namenode.replication.min: 最小的文件block副本数量,默认为1.

  • dfs.namenode.safemode.threshold-pct: 副本数达到最小要求的block占系统总block数的百分比,当实际比例超过该配置后,才能离开安全模式(但是还需要其他条件也满足)。默认为0.999f,也就是说符合最小副本数要求的block占比超过99.9%时,并且其他条件也满足才能离开安全模式。如果为小于等于0,则不会等待任何副本达到要求即可离开。如果大于1,则永远处于安全模式。

  • dfs.namenode.safemode.min.datanodes: 离开安全模式的最小可用(alive)datanode数量要求,默认为0.也就是即使所有datanode都不可用,仍然可以离开安全模式。

  • dfs.namenode.safemode.extension: 当集群可用block比例,可用datanode都达到要求之后,如果在extension配置的时间段之后依然能满足要求,此时集群才离开安全模式。单位为毫秒,默认为1.也就是当满足条件并且能够维持1毫秒之后,离开安全模式。 这个配置主要是对集群的稳定程度做进一步的确认。避免达到要求后马上又不符合安全标准。

总结一下,要离开安全模式,需要满足以下条件:
1)达到副本数量要求的block比例满足要求;
2)可用的datanode节点数满足配置的数量要求;
3) 1、2 两个条件满足后维持的时间达到配置的要求。

我们可以再次从日志中验证这些:

这里写图片描述

相关的操作命令

Hadoop提供脚本用于对安全模式进行操作,主要命令为:

hadoop dfsadmin -safemode <command>

command的可用取值如下:

command功能备注
get查看当前状态
enter进入安全模式
leave强制离开安全模式
wait一直等待直到安全模式结束

相关源码

安全模式的相关操作封装在SafeMode接口中:

/** 安全模式相关操作 */
@InterfaceAudience.Private
public interface SafeMode {
  /**
   *  检查进入或者退出安全模式的条件是否满足,
   *  如果满足,进入或退出安全模式。
   */
  public void checkSafeMode();

  /** 系统当前是否处于安全模式? */
  public boolean isInSafeMode();

  /**
   * 系统启动时是否自动进入安全模式?
   */
  public boolean isInStartupSafeMode();

  /** Check whether replication queues are being populated. */
  public boolean isPopulatingReplQueues();

  /**
   * 增加达到最小副本数要求的block数
   * @param replication current replication 
   */
  public void incrementSafeBlockCount(int replication);

  /** 降低达到最小副本数要求的block数 */
  public void decrementSafeBlockCount(Block b);
}

HDFS的命名系统需要提供安全模式相关的操作,因此实现了改接口,层次结构如下:

这里写图片描述

Namesystem继承了SafeMode接口,FSNamesystem类实现了Namesystem,间接实现SafeMode定义的操作,也就是我们可以对命名系统进行安全模式相关的操作。

FSNamesystem内部实现了一个内部线程类SafeModeMonitor,周期性地检查是否应该离开安全模式:


  /**
   * 周期性地检测是否可以离开安全模式,逻辑封装在run方法中.
   * This thread starts when the threshold level is reached.
   *
   */
  class SafeModeMonitor implements Runnable {
    /** 两次检测间隔的毫秒数,即1秒 */
    private static final long recheckInterval = 1000;

    /**
     */
    @Override
    public void run() {
      // 系统运行时,循环检测
      while (fsRunning) {
        writeLock();
        try {
          // 没有安全模式相关信息,也就是不在安全模式
          if (safeMode == null) { // Not in safe mode.
            break;//线程退出
          }
          if (safeMode.canLeave()) {
            // Leave safe mode.
            safeMode.leave();
            smmthread = null;
            // 离开安全模式后,线程退出
            break;
          }
        } finally {
          writeUnlock();
        }

        try {
          // 两次检测之间,线程休眠
          Thread.sleep(recheckInterval);
        } catch (InterruptedException ie) {
          // Ignored
        }
      }
      // 当系统不在运行的时候,线程结束退出
      if (!fsRunning) {
        LOG.info("NameNode is being shutdown, exit SafeModeMonitor thread");
      }
    }
  }

这个内部类中出现的safeMode,其类型为SafeModeInfo,用于保存安全模式下的相关信息:

 private volatile SafeModeInfo safeMode;  // safe mode information

注意到这个变量为volatile,也就是说线程对该变量的任何修改完成后,其他线程立刻可以看到变化。OK,那么什么时候会创建这个对象呢,我们用safeMode= new SafeModeInfo搜索源代码,发现2个地方会实例化这个对象:一个实在FSNamesystem的构造函数中,另一处在FSNamesystem类的enterSafeMode方法中。我们先来看构造函数,其方法声明如下:

FSNamesystem(Configuration conf, FSImage fsImage, boolean ignoreRetryCache)
      throws IOException 

这个构造函数使用给定的配置及文件镜像,创建一个文件命名系统。其中有一句如下:

this.safeMode = new SafeModeInfo(conf);

我们去看看SafeModeInfo这个构造函数:

 /**
     * Creates SafeModeInfo when the name node enters
     * automatic safe mode at startup.
     *  
     * @param conf configuration
     */
    private SafeModeInfo(Configuration conf) {
      // 这个配置就是我们前面提到的百分比配置
      this.threshold = conf.getFloat(DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY,
          DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_DEFAULT);
      if(threshold > 1.0) {
        LOG.warn("The threshold value should't be greater than 1, threshold: " + threshold);
      }
    // 这个也是我们前面提到的最小可用datanode数量配置
      this.datanodeThreshold = conf.getInt(
        DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY,
        DFS_NAMENODE_SAFEMODE_MIN_DATANODES_DEFAULT);
      this.extension = conf.getInt(DFS_NAMENODE_SAFEMODE_EXTENSION_KEY, 0);
      this.safeReplication = conf.getInt(DFS_NAMENODE_REPLICATION_MIN_KEY, 
                                         DFS_NAMENODE_REPLICATION_MIN_DEFAULT);

      LOG.info(DFS_NAMENODE_SAFEMODE_THRESHOLD_PCT_KEY + " = " + threshold);
      LOG.info(DFS_NAMENODE_SAFEMODE_MIN_DATANODES_KEY + " = " + datanodeThreshold);
      LOG.info(DFS_NAMENODE_SAFEMODE_EXTENSION_KEY + "     = " + extension);

      // default to safe mode threshold (i.e., don't populate queues before leaving safe mode)
      this.replQueueThreshold = 
        conf.getFloat(DFS_NAMENODE_REPL_QUEUE_THRESHOLD_PCT_KEY,
                      (float) threshold);
      this.blockTotal = 0; 
      this.blockSafe = 0;
    }

核心逻辑就是获取相关的配置,创建初始的SafeModeInfo。

看看另一个地方:

 /**
   * Enter safe mode. If resourcesLow is false, then we assume it is manual
   * 参数用于区分是因为资源不满足要求还是手动进入安全模式
   * @throws IOException
   */
  void enterSafeMode(boolean resourcesLow) throws IOException {
    writeLock();
    try {
      // Stop the secret manager, since rolling the master key would
      // try to write to the edit log
      stopSecretManager();

      // Ensure that any concurrent operations have been fully synced
      // before entering safe mode. This ensures that the FSImage
      // is entirely stable on disk as soon as we're in safe mode.
      boolean isEditlogOpenForWrite = getEditLog().isOpenForWrite();
      // Before Editlog is in OpenForWrite mode, editLogStream will be null. So,
      // logSyncAll call can be called only when Edlitlog is in OpenForWrite mode
      if (isEditlogOpenForWrite) {
        getEditLog().logSyncAll();
      }
      if (!isInSafeMode()) {
        safeMode = new SafeModeInfo(resourcesLow);
        return;
      }
      if (resourcesLow) {
        safeMode.setResourcesLow();
      } else {
        safeMode.setManual();
      }
      if (isEditlogOpenForWrite) {
        getEditLog().logSyncAll();
      }
      NameNode.stateChangeLog.info("STATE* Safe mode is ON"
          + safeMode.getTurnOffTip());
    } finally {
      writeUnlock();
    }
  }

总结一下,SafeModeMonitor作为守护线程,在收到来自datanode的BlockReport状态报告之后,周期性地检测是否达到离开安全模式的条件,如果符合条件,则离开安全模式。

(完)

### HDFS 详解:分布式文件系统架构与原理 #### HDFS概述 HDFS(Hadoop Distributed File System)是一种专为大数据存储和处理而设计的分布式文件系统[^2]。它是Hadoop生态系统的核心组件之一,旨在解决海量数据的可靠存储和高效访问问题。 #### HDFS的设计目标 HDFS的主要设计目标包括但不限于以下几个方面: - **大文件存储**:适合存储TB甚至PB级别的超大规模文件。 - **高吞吐量**:通过批量读写操作减少网络传输开销,提升数据访问效率[^4]。 - **容错能力**:即使部分节点发生故障,也能保证系统的正常运行。 - **可扩展性**:可以通过增加硬件资源轻松扩展存储容量和性能。 #### HDFS基本架构 HDFS采用主从结构(Master-Slave Architecture),主要由以下三个核心组件构成: 1. **NameNode** NameNodeHDFS集群的大脑,负责管理整个文件系统的命名空间和元数据信息。它记录了每个文件被分割成哪些数据块(Block),以及这些数据块分布在哪个DataNode上[^3]。此外,NameNode还接收客户端请求并协调执行各种文件操作。 2. **DataNode** DataNode是实际存储数据的工作节点,在物理磁盘上保存文件的内容片段即数据块。当启动时,它们向NameNode汇报当前持有的所有块列表;之后定期发送心跳信号以维持联系状态同时更新可用空间等动态参数[^3]。 3. **Secondary NameNode** 虽然名字里带“name”,但它并非真正的名称服务器替代品而是辅助角色——主要用于周期性合并fsimage(镜像文件)与edits log日志从而减轻primary namenode负担防止内存溢出风险但不参与实时业务流程因此严格意义上不属于HA(high availability)范畴内的备用方案[^未提及]. #### 工作原理 以下是HDFS的关键工作机制描述: - **文件写入过程**: 客户端发起上传请求给namenode,后者分配block id并将该id告知相应datanodes形成pipeline链路以便后续streaming-style write;期间每完成一个chunk size大小的数据传递后立即同步副本至其他指定位置直到达到预设replication factor为止. - **文件读取过程**: 用户定位所需resource path提交query到nn获取target blocks location info随后直接连接dn拉取content而非经由中间层转发以此降低latency提高efficiency. - **数据复制机制**: 默认情况下一份data会被copy三次分别放置于不同machine之上确保即便遭遇single point failure仍能恢复original content without loss.[^not specific] - **故障检测与恢复**: dn定时上报health status report to nn so that it can promptly detect any abnormal situation like node offline or disk corruption etc., then trigger corresponding recovery actions such as re-replicating lost chunks elsewhere within cluster boundaries accordingly.[^also not clear here] #### 性能优化特点 除了基础功能外,hdfs还有诸多针对特定场景做了专门调优的地方比如: - 支持rack awareness configuration which helps balance load across multiple racks thereby reducing inter-rack traffic costs significantly while enhancing fault tolerance at the same time.[^no exact source provided yet] - Implementing short-circuit reads when possible allows bypassing normal network channels between client and remote datanodes thus achieving faster local access speeds under certain conditions where both parties reside on identical physical hosts/machines.[^again no direct citation available currently] ```python # 示例代码展示如何简单交互hdfs api from hdfs import InsecureClient client = InsecureClient('http://localhost:9870', user='hadoop') with client.write('/example.txt') as writer: writer.write(b'Hello World\n') result = client.read('/example.txt').decode() print(result) ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值