NameNode源码学习


文章目录

  • NameNode源码学习
    • 一、文件系统目录树(第一关系)
      • 2.1 INode相关类
      • 2.2 快照特性的实现
      • 2.3 FSEditLog类
      • 2.4 FSImage类
      • 2.5 HDFS检查点机制(editlog与fsimage合并)***
    • 二、数据块管理(第二关系)
    • 三、NameNode中的租约管理
    • 四、NameNode的安全模式**
    • 五、NameNode的HA**

Java、大数据开发学习要点(持续更新中…)


一、文件系统目录树(第一关系)

  NameNode最重要的两个功能之一是维护文件系统的命名空间。HDFS文件系统命名空间在NameNode内存中是以一颗树的结构来存储的。无论是目录还是文件,在目录树中被看作一个INode节点。目录对应INodeDirectory类和文件对应的INodeFile类都是INode抽象类的具体实现类,区别在于目录类中有个成员变量children保存其子目录或文件的INode引用,而文件类则保存header字段(数据副本与block大小信息)和blocks字段(BlockInfo数组,保存文件与block、block与DN的对应关系)。

  HDFS会将命名空间保存到NameNode本地文件系统中的fsimage(命名空间镜像) 中,每次NameNode重启都会重构HDFS命名空间(FSImage类负责)。另外,对HDFS的操作会记录在editlog(操作日志) 中,以便周期性将editlog与fsimage进行合并生成新的fsimage,editlog同样会存储在NameNode本地文件系统中(FSEditLog类管理)。

2.1 INode相关类

  借鉴Linux中的inode(索引节点),索引节点保存文件的元信息(如文件类型、权限、文件长度等),在索引节点的后半部分存放数据块索引。HDFS将文件和目录的抽象类命名为INode,其主要实现类在上文中已经介绍,不再赘述。

  • INode抽象类实现了一些接口,定义了一些元信息的get与set方法和唯一一个字段parent。

INode类的设计采用了模板设计模式。里面的方法多为两个,其中一个是final的接口方法作为规范接口的调用不希望子类继承的;另一个是abstract的抽象方法,留给子类具体实现。这种设计模式的思想就是将不变的行为/流程/算法定义在父类中,去除子类的冗余

//留给子类具体实现
abstract void setUser(String user);
//模板方法,final修饰,不可继承,供子类接口调用
final INode setUser(String user,Int latestSnapshotId){//其他方法setUser(user); //子类实现方法return this;
}
  • INodeWithAdditionFields类顾名思义,作为INode抽象类的子类,实现了里面字段的set方法,定义了一些INode元信息。其中比较有意思的是将用户信息、用户组信息和权限信息用一个long类型的permission字段来表示,通过静态枚举类来解析这个字段中包含的信息,节省了INode对象的内存占用。HDFS2.6版本后,将例如磁盘配额、正在构建、快照等功能抽象成features集合字段。
  • INodeDirectory类抽象了HDFS中的目录。在INodeDirectory类的实现中,增加了成员变量children,是一个list集合用来存放目录下的目录与文件的INode对象。类中定义了对children、特性和快照的增删改查方法。
  • INodeFile类抽象了HDFS中的文件。其中header(文件头)blocks(文件对应数据块信息) 字段最为重要。文件头是long类型,类似上文中permission字段,可以被解析成存储策略、备份数、数据块大小信息。blocks是一个BlockInfo类的数组,保存了文件与数据块、数据块与数据节点的对应关系。类中的方法主要涉及构建特性和快照特性的相关方法。
  • INodeReference与其内部子类一起解决带有快照的目录、文件重命名或移动后的访问问题。

2.2 快照特性的实现

  管理员通过客户端执行“hdfs dfsadmin-allowSnapshot”命令在目录a上开启快照功能后,HDFS会创建一个DirectorySnapshottableFeature对象,然后将这个Feature对象添加到目录a对应的INodeDirectory对象的features集合中,如下图所示:

  在成功开启目录a快照功能后,管理员可以执行“hdfs dfs-createSnapshot”命令在目录a下创建一个快照s1。HDFS会创建一个DirectoryDiff对象记录s1快照创建后目录a上执行的所有操作,并将这个对象添加到DirectorySnapshottableFeature的DirectoryDiffList对象中保存。DirectoryDiff会通过持有一个ChildrenDiff对象记录目录a的子目录项的变化情况,ChildrenDiff对象中的c-list集合保存了快照创建后目录a下所有添加的文件和目录,d-list集合保存了快照创建后目录a下删除的文件或目录。例如,从目录a中删除文件e时,文件e并不会被直接从INodeDirectory中完全删除,而是暂时保存在了快照s1中DirectoryDiff对象的d-list中,如下图所示:

在删除文件e后,再在目录a下创建一个快照s2,然后向目录a添加一个文件g,HDFS会在DirectorySnapshottableFeature对象上添加一个新的DirectoryDiff对象记录快照s2创建后在目录a上的所有操作,这样s1对应的DirectoryDiff对象就记录了从快照s1到快照s2间对目录a的所有操作。在创建s2快照后,在目录a下创建新文件g,会放入s2快照对应的c-list中,如下图所示:

注:此时在s1下访问文件e是可以返回的,在s2下访问文件g返回是空。

2.3 FSEditLog类

  在NameNode中,fsimage是用于持久化命名空间的二进制文件。fsimage将文件系统目录树中的每个文件或者目录信息保存为一条记录(包含名称、大小、用户、用户组、修改时间、创建时间等信息)。NameNode每次重启都会根据fsimage文件来重构命名空间。但是fsimge是磁盘文件,如果NameNode频繁更新fsimage势必影响执行效率,因此,HDFS通过将操作记录写在editlog文件中,editlog会随着NameNode的运行实时更新,HDFS定期将editlog和fsimage合并来保持fsimage与NN内存中的命名空间的同步。

FSEditLog类被设计成一个状态机(初始化、新日志未开始、可写、可读、结束),并利用底层输入输出流实现editlog的快速写出(输出流使用双buffer机制并使用同步锁,实现不同线程间输出到缓存和缓存同步到磁盘的并行)

2.4 FSImage类

  从前文知道,fsimage是用于持久化命名空间的二进制文件。NameNode会在启动时加载fsimage到内存中,HDFS定期将editlog和fsimage合并成新的fsimage。而FSImage类主要实现了以下功能:保存命名空间、加载fsimage文件和加载editlog文件。

fsimage文件是通过FSImageFormatProtobuf.Saver来将命名空间序列化后保存的,所以fsimage文件是protobuf格式。同样的,fsimage文件通过FSImageFormatProtobuf.Loader加载到内存。

2.5 HDFS检查点机制(editlog与fsimage合并)***

  在NameNode启动过程中,需要将fsimage和editlog加载到内存中,逐条执行editlog中的操作完成命名空间的合并。如果editlog文件过大,会导致NameNode启动时间过长,由此引入了检查点机制(checkpointing)。

  由于合并fsimage和editlog是一项非常消耗I/O和CPU资源的操作,同时NameNode还需要限制其他用户对HDFS的访问和操作。为了防止这种情况,HDFS将检查点操作放在Secondary NameNode(非HA)或者Standby NameNode(HA)上执行检查点的触发条件则是:(1)超过配置检查点操作时长后触发 (2)事务(Transaction)超过配置数。

下面介绍非HA下检查点机制和HA下检查点机制的实现:

  1. Secondary NameNode执行检查点操作(非HA)
  • 首先,SN检查两个触发条件是否满足其一。在非HA模式下,由于SN与NN间没有共享editlog文件目录,所以最新的事务id是SN通过RPC请求NamenodeProtocol.getTransactionId()向NN获取的。
  • SN调用RPC方法NamenodeProtocol.rollEditLog()触发editlog重置,结束当前在写的editlog段落,并创建新的edit.new文件。同时,返回重置editlog文件和fsimage的事务id(seen_id,上次的检查点)。这样,NN新的操作可以写入edit.new文件中不影响editlog记录功能。
  • 获得seen_id和最新事务id后,SN发起HTTP GET请求到NN获取新的fsimage和editlog文件
  • SN加载下载完成的fsimage并与读取的editlog合并写入新的fsimage文件中。
  • SN向NN发送HTTP GET请求,URL中告知NN新fsimage的事务id以及SN用于下载的IP和端口号
  • NN根据SN提供的信息,通过HTTP GET请求下载fsimage文件,并用MD5校验。
  1. Standby NameNode执行检查点操作(HA)
    首先看下HA模式下的HDFS集群,如下图所示:

    集群中会有两个NN同时运行,Active NN用于实时处理客户端请求,Standby NN命名空间与Active NN保持一致,在其出现故障后切换成Active状态。Active NN在执行任何修改命名空间的操作时,需要将产生的editlog持久化到集群中奇数个JN中(需要半数以上的JN持久化了数据并反馈才算成功)。Standby NN监听JNs中editlog的变化,然后合并更新自己的命名空间。当满足触发检查点的条件后,Standby NN将命名空间写入新的fsimage文件中,通过HTTP GET请求,Active NN根据请求中的事务id、IP和端口号下载fsimage文件。

二、数据块管理(第二关系)

  NameNode的另一个重要功能是数据块与数据节点的管理,即指定数据块副本保存在哪些数据节点上。这个信息是DataNode启动时主动上报给NN的,所以是动态构建的。下面是NN第二关系的内存管理图:

  • Block类:用来唯一标识NN中的数据块,是HDFS数据块最基本的抽象接口。Block类实现Writable接口和Comparable接口,可以序列化和按照blockid排序。Block类中定义三个字段blockId(标识对象)、numBytes(数据块大小)、generationStamp(时间戳)。
  • BlockInfo类:扩展至Block类,其中bc字段保存数据块属于哪个HDFS文件,triplets是一个Object数组,通过特殊的实现完成通过DatanodeStorageInfo对象双向链表获得这个DataNode所有的BlockInfo对象,比LinkedList实现更加节省内存空间。
  • BlocksMap类:用来动态构建数据块和数据节点的对应关系,内部维护Block到BlockInfo的映射关系,因为BlockInfo保存的信息都是DN在启动时上报的,NN内存中保存的关于数据块的信息只有Block类中维护的那么多。
  • Replica类:对DN中副本的描述 ,要注意HDFS中NN中的数据块叫数据块,DN中数据块叫做副本。
  • BlockManager类:保存并管理了HDFS集群中所有数据块的元数据(数据块增删改查管理、BlocksMap构建、块汇报管理)。

对上面进行简短的总结:NN启动后会加载持久化的fsimage和editlog文件重建文件系统目录树。但是,对于数据块和DN的映射关系需要在DN上报后动态构建。DN在启动时除了会和NN握手、注册以及上报数据块信息外,还会定时向NN发送心跳以及块汇报,并执行NN传回的指令。

下面是NN对DN管理的相关类:

  • DatanodeDescriptor类:数据节点描述符,是NN中对DN的抽象。继承于DatanodeInfo(容量、状态)和DatanodeId(地址、端口、名称)。为了支持异构存储(DN上可以有多种存储结构),用DatanodeStorageInfo类描述DN上的一种存储,其中blockList是一个BlockInfo类型链表的头节点,链表维护了DN上该种存储的BlockInfo对象。
  • DatanodeManager类:管理DN注册、撤销、心跳等流程的实现。

几个注意点:

  1. 第一关系是NN的文件系统树,是持久化在磁盘上的fsimage和editlog记录的,在NN启动的同时就需要读取并合并构建在内存中。第二关系是block与对应DN(新版本是DatanodeStorageInfo)的关系,需要在DN启动后向NN汇报动态构建。
  2. DN的汇报分为第一次的全量块汇报、周期性的全量块汇报(避免增量块汇报的失效)和增量块汇报。
  3. NN对DN心跳信息的进行处理,返回对DN的指令;NN对所有DN进行心跳检测,排除故障节点。

三、NameNode中的租约管理

  租约机制实际是HDFS的文件写锁,规定租约持有者(一般是客户端)在规定的时间内拥有对某些文件写的权限。

  租约实现与租约恢复:租约由Lease类定义,其中字段holer保存租约持有者信息,paths保存持有文件的路径,lastUpdate保存租约最后更新时间。这些由NN中的租约管理类进行租约管理,并维护了一个Monitor类定期检查租约是否超过硬限制时间。

  租约恢复主要有以下三种情况:(1)Monitor线程检测到租约超过硬限制,进行租约恢复强制回收租约。(2)当有新的客户端进行写或者追加操作时,发现文件租约已经被持有,并且租约时间已经超过软限制时间未更新,则会恢复租约并持有租约。(3)客户端完成写文件后主动恢复租约。

租约恢复时,需要检查文件最后一个block的状态,需要对其进行检查和修复(如果是正在构建或恢复状态,需要去在所有存有对应副本的数据节点中找到状态最好的副本给所有节点进行数据同步)。

四、NameNode的安全模式**

  前面讲了NN中的第一关系和第二关系,安全模式就是在NN启动时对这两个关系的构建过程

  1. NN启动时不接受任何对命名空间的修改指令,也不触发任何对数据块的操作。
  2. 启动后,NN首先加载持久化的fsimage和editlog进行合并,构建内存中文件系统树(第一关系)。
  3. 之后,NN接收DN的块汇报,动态构建文件block与对应数据节点的关系(第二关系)。这个过程会有设定阈值,文件总的block数量能在第一关系构建后得知,而达到最小副本数的副本数量会在这个阶段动态更新,一旦达标副本数超过阈值,NN等待时间一段时间后退出安全模式。

手动进入安全模式需要手动退出(手动设置的底层会使等待时间设置为INTEGER.MAX_VALUE)

五、NameNode的HA**


  NameNode的HA架构主要通过Active NameNode(下简写ANN)和Standby NameNode(下简写SNN)的故障切换进行。那么这就涉及ANN与SNN的第一关系和第二关系一致性问题,我们期望ANN宕机后SNN内存中有与ANN尽可能相近的所有元信息实现热备
  解决第一关系同步的方式:NN的HA架构中用群体日志(Quorum Journal)作为共享日志的方案,ANN定期将执行的操作记录在editlog中并写入多数JNs节点中,SNN监听JNs上editlog的变化,并与自己的命名空间合并。这里用分布式一致性协议实现了JNs的数据一致性,提供服务的JNs总是奇数2N+1台,只有大多数JN(大于N+1)返回写入成功,才认为此次写入成功(最多允许N台机器宕机)。JNs与NN的信息交换都是通过NN上的QuorumJournalManager调用RPC请求实现的。
  解决第二关系同步的方式:SNN节点保存实时数据块存储信息的方式,是通过DN心跳与块汇报会同时发送给ANN和SNN实现的(但只有ANN能给DN下达指令)。
  主备切换:每个NN节点对应运行ZKFC(ZKFailoverController,ZK轻量级客户端),其中用HealthMonitor中的内部线程调用NN的HAServiceProtocol RPC接口监控NN健康状态并汇报。如果检测到NN健康状态发生变化,ZKFC通过ActiveStandbyElector向ZK集群交互完成主备选举,并通过RPC通知NN进行主备切换。
  主备切换的脑裂预防:主备切换后由于可能如网络问题产生的出现多个ANN的问题(脑裂问题),采用隔离(fencing)机制进行预防(实际是数值增加的机制)。

  1. 共享存储隔离:同一时间只允许一个NN向JNs写入editlog,是通过epoch number实现的,在主备切换后新任ANN持有epoch number在原基础上+1,JNs会拒绝epoch number小于此数值的NN写入,由此防止假死NN重新活跃写入日志的问题。
  2. 数据节点隔离:同一时间只允许一个NN向DN下达指令,本质是NN会向DN发送序列号和状态,NN中的序列号大于等于DN中的序列号才能下达指令,主备切换后序列号增大,假死的NN由于序列号过小无法下达指令。
  3. 客户端隔离:同一时间只允许一个NN响应客户端请求。

参考:Hadoop 2.X HDFS源码剖析 徐鹏著

Hadoop HDFS源码学习之NameNode部分相关推荐

  1. 第七章:小朱笔记hadoop之源码分析-hdfs分析 第四节:namenode分析-namenode启动过程分析...

    第七章:小朱笔记hadoop之源码分析-hdfs分析 第四节:namenode分析 4.1 namenode启动过程分析 org.apache.hadoop.hdfs.server.namenode. ...

  2. 第七章:小朱笔记hadoop之源码分析-hdfs分析 第四节:namenode-LeaseManagerMonitor

    第七章:小朱笔记hadoop之源码分析-hdfs分析 第四节:namenode分析 4.4 namenode文件租约分析LeaseManagerMonitor 文件租约就是将操作的文件和操作它的客户端 ...

  3. 第七章:小朱笔记hadoop之源码分析-hdfs分析 第三节:hdfs实现分析

    第七章:小朱笔记hadoop之源码分析-hdfs分析 第三节:hdfs实现分析 3.3 namenode (1)FSDirectory FSDirectory用来管理HDFS整个文件系统的namesp ...

  4. 第七章:小朱笔记hadoop之源码分析-hdfs分析 Datanode 心跳分析

    第七章:小朱笔记hadoop之源码分析-hdfs分析 第五节:Datanode 分析 5.2 Datanode 心跳分析 (1)offerService分析 写道 (a)检查心跳间隔是否超时,如是向n ...

  5. 第七章:小朱笔记hadoop之源码分析-hdfs分析 第五节:Datanode 分析

    第七章:小朱笔记hadoop之源码分析-hdfs分析 第五节:Datanode 分析 5.1 Datanode 启动过程分析 5.2 Datanode 心跳分析 5.3 Datanode 注册分析 5 ...

  6. HDFS源码解析:教你用HDFS客户端写数据

    摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...

  7. 第二章:小朱笔记hadoop之源码分析-脚本分析

    第二章:小朱笔记hadoop之源码分析-脚本分析 第一节:start-all.sh 第二节:hadoop-config.sh 第三节:hadoop-env.sh 第四节:start-dfs.sh 第五 ...

  8. HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程

    在<HDFS源码分析心跳汇报之数据结构初始化>一文中,我们了解到HDFS心跳相关的BlockPoolManager.BPOfferService.BPServiceActor三者之间的关系 ...

  9. HDFS源码分析心跳汇报之数据结构初始化

    在<HDFS源码分析心跳汇报之整体结构>一文中,我们详细了解了HDFS中关于心跳的整体结构,知道了BlockPoolManager.BPOfferService和BPServiceActo ...

最新文章

  1. 【 MATLAB 】Signal Processing Toolbox Functions - By Category
  2. Linux NFS服务器的安装与配置
  3. 【区块链】GO语言区块链项目——超级账本
  4. CentOS系列启动流程和内核原理(5系列,6系列,7系列)
  5. Spring MVC – HTTP消息转换器
  6. mybatis SqlMapConfig.xml
  7. MEME将于4月12日推出V2版本
  8. 配置vivado用vscode编辑文本
  9. Eureka-Client(Golang实现)
  10. E-Prime 2.0 用了一段时间出现警告信息无法编辑实验程序
  11. mysql中varbinary什么意思_sql中varbinary 是什么数据类型
  12. java怎么实现事务_java实现简单的事务
  13. AUTOCAD——比例缩放
  14. 我爱淘二次冲刺阶段2
  15. Google SketchUp Cookbook: (Chapter 3) Intersection Edges: Cutting and Trimming
  16. 计算机图形学学习笔记(5.1)几何造型与样条
  17. matlab迭代实现矩阵运算,用matlab实现Rayleigh迭代计算矩阵特征值的程序
  18. 51单片机教程:8*8 点阵显示字符、数字、简单汉字
  19. pjsip安卓端编译步骤
  20. 交通运输大数据发展特点、政策、应用及趋势 | 交通运输部科学研究院黄莉莉

热门文章

  1. CPU一致性的解决办法
  2. 检测、识别类算法性能指标简介
  3. 如何选择适合自己的吉他弦(上)
  4. potato土豆登录不了_皮到爆炸的搞笑句子朋友圈 现在的心情就是去了皮的大土豆...
  5. GEM/SECS设备自动化和EAP自动化软件
  6. 通达信行情接口是什么端口
  7. java基于keda事件驱动的k8s 容器自动伸缩
  8. 时间轮算法(TimingWheel)
  9. 史上最完整交互设计基本原则
  10. 洛谷:P2700 逐个击破(最大生成树、贪心)