HDFS源码分析之LightWeightGSet

        LightWeightGSet是名字节点NameNode在内存中存储全部数据块信息的类BlocksMap需要的一个重要数据结构,它是一个占用较低内存的集合的实现,它使用一个数组array存储元素,使用linked lists来解决冲突。它没有实现重新哈希分区,所以,内部的array不会改变大小。这个类不支持null元素,并且不是线程安全的。它在BlocksMap中的初始化如下:

this.blocks = new LightWeightGSet<Block, BlockInfo>(capacity)// ...省略部分代码

        可见,它类似一个Key、Value集合,Key为BLock对象,Value为BlockInfo对象。

        那么,对于LightWeightGSet的上述介绍该如何解释呢?既然看上去像一个Key、Value集合,那么它到底是不是一个Key、Value集合呢?而且,为什么说它占内存比较低,使用数组存储元素,又用linked lists来解决冲突呢?相信当你看完LightWeightGSet的实现,就会一目了然。我们先看下LightWeightGSet中最重要的一个成员变量,如下:

  /**
   * An internal array of entries, which are the rows of the hash table.
   * The size must be a power of two.
   * 存储元素条目的内部数组,它是哈希表中的行。
   * 数组大小必须是2的幂。
   * 数组元素实现了LinkedElement接口。
   */
  private final LinkedElement[] entries;

        entries是一个存储实现了LinkedElement接口对象的数组,实际上存储的是BlockInfo实例。它是LightWeightGSet存储元素条目的内部数组,数组中元素是哈希表中的一行,且数组的大小必须为2的幂。

        先了解上述信息就行,我们再往下看,既然是一个集合,就得能够实现存取元素,LightWeightGSet肯定得对外提供了元素存取的方法,先看存,通过put()方法实现,代码如下:

  @Override
  public E put(final E element) {
    //validate element
	// 检验元素element
	
	// 不支持null元素
    if (element == null) {
      throw new NullPointerException("Null element is not supported.");
    }
    
    // 元素必须实现LinkedElement接口
    if (!(element instanceof LinkedElement)) {
      throw new HadoopIllegalArgumentException(
          "!(element instanceof LinkedElement), element.getClass()="
          + element.getClass());
    }
    
    // 将元素element强制转换成LinkedElement类型实例e
    final LinkedElement e = (LinkedElement)element;

    //find index
    // 获取元素对应索引
    // 实际上是根据block的hashCode和hash_mask的一种循环取余算法
    // blockID是一个递增的序列,它在数组内的index也是在数组长度范围内递增的
    final int index = getIndex(element);

    //remove if it already exists
    // 如果元素已经存在的话,移除
    final E existing = remove(index, element);

    //insert the element to the head of the linked list
    // 将元素element插入到linked列表的头部
    
    // 累加modification、size
    modification++;
    size++;
    
    // 元素设置
    // 将e的next元素设置为数组当前index位置的元素
    e.setNext(entries[index]);
    // 将e的next元素设置为数组当前index位置的元素
    entries[index] = e;

    // 返回之前存储的元素existing
    return existing;
  }
        put()方法实现了LightWeightGSet存数据的功能,它接收一个E泛型element作为参数,实现逻辑如下:

        1、首先校验参数,即需要存储的元素element,它必须满足不能为null和必须实现LinkedElement接口两个限制条件;

        2、然后将元素element强制转换成LinkedElement类型实例e;

        3、调用getIndex()方法根据元素element获取它在数组entries中对应的位置索引index;

        4、如果元素存在的话,调用remove()方法,根据位置索引index和元素remove进行移除操作,并得到有可能之前存储的元素existing;

        5、累加modification、size:

              modification代表了数据修改量,无论增加还是删除,均会累加;

              size代表了集合元素个数,增加元素时即累加,删除元素时即累减;

        6、进行元素设置:

              6.1、将e的next元素设置为数组当前index位置的元素,可能为null,也可能为之前存在的不等元素,但肯定不是和需要添加元素相等的元素,因为如果存在,上面就已经删除了;

              6.2、将当前元素e设置为数组当前index位置的元素;

        7、将当前元素e设置为数组当前index位置的元素。
        通过上述添加元素过程的逻辑介绍,你是不是能体会到以下这点呢:

        LightWeightGSet在内存中本质上是一个数组entries,用于存储实现了LinkedElement接口的元素。当添加元素element时,我们能够根据待添加元素element计算出它在数组entries中的位置索引index,然后根据位置索引index和元素element删除之前可能存在的相等元素,然后再进行元素设置,将数组entries中当前位置索引index处的元素设置为待添加元素element的next元素,而将待添加元素element放置到数组entries中的位置索引index处。

        看到这里,你是不是恍然大悟,是不是能感受到LightWeightGSet中的数组中每个位置存储的好像是一个列表,而不是单一的一个元素?如果你能体会到这点,你就能开始领会到LightWeightGSet的真谛了。而且,我们已经能够开始回答上面我们遗留的使用一个数组array存储元素,使用linked lists来解决冲突这个疑问了。

       

<think>嗯,用户想要了解HDFS源码分析。首先,我需要确定用户的具体需求。HDFS是Hadoop分布式文件系统的核心组件,源码分析可能涉及到架构、关键模块、读写流程等方面。用户可能是开发者或者系统管理员,希望深入理解HDFS的内部机制,以便优化或解决问题。 用户可能已经有一定的HDFS使用经验,但缺乏对源码的了解。他们可能想了解如何入手分析源码,或者重点模块在哪里。需要确保回答结构清晰,分步骤引导,同时遵循系统级指令中的格式要求,比如正确使用LaTeX。 首先,我应该介绍HDFS的基本架构,帮助用户建立整体认识。然后分模块讲解,比如NameNode、DataNode、客户端通信等。每个部分需要说明源码的位置和关键类,例如NameNode的启动流程涉及的类。 接下来,可能需要详细说明读写流程,包括源码中的关键方法,如create()和read()。这部分要结合具体的包和类,帮助用户定位代码位置。另外,元数据管理和持久化也是重点,比如FsImage和EditLog的处理。 通信机制部分,RPC和流式接口是HDFS的核心,需要提到相关的接口和实现类,如ClientProtocol和DataTransferProtocol。故障恢复与高可用部分,可以介绍主从切换和日志恢复机制,比如ZKFC和EditLogTailer。 最后,提供一些源码分析的建议,比如调试环境搭建和关键断点设置。还要注意提醒用户参考官方文档和社区资源,确保信息的准确性。 需要检查LaTeX格式是否正确,所有行内公式用$...$,独立公式用$$...$$,但根据内容来看,可能数学公式较少,但如果有示例的话需要正确应用。同时保持语言口语化,避免使用Markdown格式,分步骤和模块时用序号和标题清晰标明。</think>以下是对HDFS源码分析的层次化解析,结合架构设计与关键模块实现原理: #### 一、HDFS架构核心 1. **模块划分** - `NameNode`:元数据管理(`FSDirectory`类记录目录树,`INode`描述文件/目录元数据) - `DataNode`:数据块存储(`BlockScanner`实现周期性校验) - `JournalNode`:高可用日志同步(基于Paxos算法) - 通信协议:RPC接口(`ClientProtocol`)与流式接口(`DataTransferProtocol`) 2. **源码目录结构** - 主入口:`hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs` - 关键子包: - `server/namenode`:NameNode核心逻辑 - `server/datanode`:DataNode实现 - `protocol`:RPC协议定义 - `client`:HDFS客户端 #### 二、关键流程源码分析 1. **写文件流程**(`DFSOutputStream`类) ```java // 创建数据流管道 List<DatanodeInfo> targets = namenode.addBlock(...); // 数据分包传输 packet.writeTo(streamer); // 应答确认 waitForAckedSeqno(seqno); ``` 2. **读文件流程**(`DFSInputStream`类) - 通过`getBlockLocations()`获取块位置 - 采用短路读优化(`ShortCircuitLocalRead`) 3. **元数据持久化** - `FsImage`序列化格式:使用Protobuf(`NSection$NameSystemSection`) - EditLog合并:`Checkpointer`线程定期执行 #### 三、高可用实现 1. **主从切换机制** - `ZKFC`通过ZooKeeper监控健康状态 - 脑裂防护:`QJM`基于quorum journal实现 2. **日志同步流程** ```java // JournalNode处理日志写入 public void journal(RequestInfo reqInfo, long segmentTxId...) throws IOException { validateRequest(reqInfo); log.write(op); } ``` #### 四、性能优化点 1. **内存管理** - NameNode堆外内存:`OffHeapINodeMap`减少GC压力 - 块报告合并:`BPServiceActor`实现增量报告 2. **网络优化** - 零拷贝传输:`transferTo()`实现DMA直接内存访问 - 管道化写入:`Packet`队列异步发送 #### 五、调试建议 1. 搭建源码环境: ```shell mvn install -DskipTests -Pdist,native ``` 2. 关键断点设置: - NameNode启动:`NameNode.loadNamesystem()` - 块分配:`BlockManager.addBlock()` 建议配合官方JIRA(HDFS-XXX编号问题)和《Hadoop技术内幕》系列书籍进行扩展研究,重点关注HDFS-3.x版本引入的EC纠删码和Router-Based Federation实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值