Namenode 会定期将内存中的命名空间(文件目录树、文件目录元信息)保存到 fsimage 文件中,以防止 Namenode 掉电或者进程崩溃。如果 Namenode 实时地将内存中的元数据同步到 fsimage 文件中,会非常消耗资源且造成 Namenode 运行缓慢。所以 Namenode 会先将命名空间的修改操作保存在 editlog 文件中,然后定期合并 fsimage 和 editlog 文件。

一个正常大小的 editlog 文件往往在几十到几百个字节之间,但在某些极端的情况下,editlog 文件会变得非常大,甚至将磁盘空间写满,在 Namenode
启动过程中一个很重要的部分就是逐条读取 editlog 文件中的记录,之后与 Namenode 命名空间合并。巨大的 editlog 文件会导致 Namenode 的启动时间过长,为了解决这个问题,HDFS 引入了检查点机制 (checkpointing)。

一、checkpoint 机制概述

HDFS 的检查点机制会定时将 editlog 文件与 fsimage 文件合并以产生新的 fsimage 文件。这样 Namenode 就可以直接从 fsimage 加载元数据,而不用读取与合并 editlog 中的记录了。有了检查点机制,Namenode 命名空间的重建效率会有效提高,同时 Namenode的启动时间也会相应减少。

但是合并 fsimage 与 editlog 以产生新的 fsimage 文件是一项非常消耗 I/O 和 CPU 资源的操作。在执行检查点操作期间,NameNode 还需要限制其他用户对 HDFS 的访问和操作。为了预防这种情况的发生,HDFS 将检查点操作从 Active Namenode 移动到了 Secondary Namenode 或者 Standby Namenode 上,至于具体是哪一种 Namenode,则取决于当前的 HDFS 是否开启了 HA 功能。

在以下两种情况下,Namenode 会触发一次检查点操作

  • 超过了配置的检查点操作时长 (dfs.namenode.checkpoint.period 配置项配置)
  • 从上一次检查点操作后,发生的事务(transaction) 数超过了配置(dfs.namenode.checkpoint.txns 配置项配置)。

1.1. Secondary Namenode 检查点操作

在非 HA 部署环境下,检查点操作是由 Secondary Namenode 来执行的。Secondary Namenode 是 HDFS1.X 版本中一个非热备的 Namenode 备份节点,整个检查点流程:

  • Secondary Namenode 检查两个触发检查点流程的条件是否满足。

  • Secondary Namenode 调用 RPC 方法触发 editlog 重置操作,将当前正在写的 editlog 段落结束,并创建新的 edit.new 文件,这个操作还会返回当前 fsimage 以及刚刚重置的 editlog 的事务 id (seen_id)。这样当 Secondary Namenode 从 Namenode 读取 editlog 文件时,新的操作就可以写入 edit.new 文件中,不影响 editlog 记录功能。

    在 HA 模式下,并不需要显式地触发 editlog 的重置操作,因为 Standby Namenode 会定期重置 editlog。

  • 有了最新的 txid 以及 seen_id, Secondary Namenode 就会发起 HTTP GET 请求到 Namenode 的 GetlmageServlet 以获取新的 fsimage 和 editlog 文件。

    需要注意,Secondary Namenode 在进行上一次的检查点操作时,可能己经获取了部分 fsimage 和 edits 文件。

  • Secondary Namenode 会加载新下载的 fsimage 文件以重建 Secondary Namenode 的命名空间。

  • Secondary Namenode 读取 edits 中的记录,并与当前的命名空间合并,这样 Secondary Namenode 的命名空间和 Namenode 的命名空间就同步了。

  • Secondary Namenode 将最新的同步的命名空间写入新的 fsimage 文件中。

  • Secondary Namenode 向 Namenode 的 ImageServlet 发送 HTTP GET 请求/getimage?putimage=1。这个请求的 URL 中还包含了新的 fsimage 文件的事务ID,以及 Secondary Namenode 用于下载的端口和IP 地址。

  • Namenode 会根据 Secondary Namenode 提供的信息向 Secondary Namenode 的 GetlmageServlet 发起 HTTP GET 请求下载 fsimage 文件。Namenode 首先将下载文件命名为 fsimage.ckpt_ txid ,然后创建 MD5 校验和,最后将 fsimage.ckpt_ txid 重命名为 fsimage_txid。

至此,一个完整的 fsimage 的检查点操作就完成了。

1.2. Standby Namenode 检查点操作

HDFS2.X 版本中加入了 Namenode HA 策略,这使得 HA 机制下检查点操作的流程与非 HA 机制下的完全不同,因为在 HA 机制下会同时运行两个 Namenode 实例,其中一个为 ActiveNamenode,用于实时处理所有客户端请求;另一个为 Standby Namenode, Standby Namenode的命名空间与 ActiveNamenode 完全保持一致。当 ActiveNamenode 出现故障时,Standby Namenode 可以立即切换成 Active 状态。

HA 配置下己经没有 Secondary Namenode 这个节点了,而是直接通过配置奇数个 JournalNode 来实现 Namenode 热备 HA 策略。

为了让 Standby Namenode 的命名空间与 Active Namenode 保持同步,它们都将和 JournalNodes 守护进程通信。当 Active Namenode 执行任何修改命名空间的操作时,它至少需要将产生的 editlog 文件持久化到N-(N-1)/2 个 JournalNode 节点上才能保证命名空间修改的安全性。

Standby Namenode 则负责观察 editlog文件的变化,它能够从 JournalNodes中读取 editlog 文件,不断读入的 editlog 文件与当前的命名空间合并。所以Standby Namenode 始终保持着一个最新版本的命名空间。一旦 Active Namenode 出现故障,Standby Namenode 就会保证从 JournalNodes 中读出全部的 editlog 文件,然后切换成 Active 状态。

二、Standby Namenode 检查点机制实现

我们看一下 HDFS 代码是如何实现检查点功能的~~~Namenode启动时会调用 HAState.enterState()

在 HA 部署环境下,检查点操作是由 StandBy Namenode 来执行的。StandBy Namenode 是 HDFS2.X 版本中一个热备的 Namenode 备份节点

截屏2022-08-15 22.21.30

按照时序图点点点~~~

来到核心代码 FSNamesystem.startStandbyServices()~

2.1. 日志系统的初始化

FSNamesystem.startStandbyServices() 调用 FSEditLog#initJournals() 进行日志系统的初始化

1
2
3
if (!getFSImage().editLog.isOpenForRead()) {
getFSImage().editLog.initSharedJournalsForRead();
}

initJournals() 方法会根据传入的 dirs 变量(保存的是 editlog 文件的存储位置) 初始化 journalSet 字段 (JoumalManager对象的集合)。初始化之后,FSEditLog 就可以调用 journalSet 对象的方法向多个日志存储位置写 editlog 文件了

在 NameNode 启动阶段,会调用 startActiveServices(),点点点~~~,其中会调用 FSEditLog#initJournals() 进行日志系统的初始化

initJournals() 方法会根据传入的 dirs 变量(保存的是 editlog 文件的存储位置) 初始化 journalSet 字段 (JoumalManager对象的集合)。初始化之后,FSEditLog 就可以调用 journalSet 对象的方法向多个日志存储位置写 editlog 文件了。

初始化日志系统,根据本地和远程情况,把对应的JournalManager添加到JournalSet里面,这样后面再写日志的时候,就会调用这里面的流

2.1.1. 设置 edits 文件的最小数量

默认值 为 1

1
2
3
int minimumRedundantJournals = conf.getInt(
DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_MINIMUM_KEY,
DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_MINIMUM_DEFAULT);

22

2.1.2. 遍历 editsDirs 目录

  1. 本地系统

    如果是本地文件系统,则创建 FileJournalManager 对象,这个对象专门管理元数据写入namenode

    1
    journalSet.add(new FileJournalManager(conf, sd, storage), required, sharedEditsDirs.contains(u));
  2. JournalNode

    如果不是本地系统,那么会 createJournal -->QuorumJournalManager,这个对象专门管理元数据写入 Journalnode

    1
    journalSet.add(createJournal(u), required, sharedEditsDirs.contains(u));

JournalSet 就是存放一系列的 JournalAndStream 的容器,对于容器中的一个元素 JournalAndStream 表示一个 JournalManager 和一个输出流

2.2. 创建 editLogTailer 线程

editLogTailer 线程用来从远程的 QuorumJournalManager 服务器上拉取editlog,重演到自己到内存

2.3. 创建 Checkpointer 线程

StandbyNamenode 会持有一个 StandbyCheckpointer 类,这个类维护着一个叫作 Checkpointer Thread 的线程

三、总结

HA 配置下己经没有 Secondary Namenode 这个节点了,而是直接通过配置奇数个 JournalNode 来实现 Namenode 热备 HA 策略。为了让 Standby Namenode 的命名空间与 Active Namenode 保持同步,它们都将和 JournalNodes 守护进程通信。当 Active Namenode 执行任何修改命名空间的操作时,它至少需要将产生的 editlog 文件持久化到N-(N-1)/2 个 JournalNode 节点上才能保证命名空间修改的安全性。

Standby Namenode 则负责观察 editlog文件的变化,它能够从 JournalNodes中读取 editlog 文件,不断读入的 editlog 文件与当前的命名空间合并。所以Standby Namenode 始终保持着一个最新版本的命名空间。一旦 Active Namenode 出现故障,Standby Namenode 就会保证从 JournalNodes 中读出全部的 editlog 文件,然后切换成 Active 状态。

检查点机制在 HA 模式下基本流程:

  • Standby Namenode 检查是否满足触发检查点操作的两个条件。

  • Standby Namenode 将当前的命名空间保存到 fsimage.ckpt_ txid 文件中,这里的 txid 是当前最新的 editlog 文件中记录的事务id。之后 Standby Namenode 写入 fsimage 件的 MD5 校验和,最后重命名这个 fsimage.ckpt_txid 文件为 fsimage_txid。

    当执行这个操作时,其他的 Standby Namenode 操作将会被阻塞

  • Standby Namenode 向 Active Namenode 的 ImageServlet 发送 HTTP GET 请求 /getimage?putimage=l。这个请求的 URL 中包含了新的 fsimage 文件的事务 id,以及Standby Namenode 用于下载的端口和 IP 地址。Active Namenode 会根据 Standby Namenode 提供的信息向 Standby Namenode 的ImageServlet 发起 HTTP GET 请求下载 fsimage 文件。Namenode 首先将下载文件命名为 fsimage.ckpt,然后创建 MD5 校验和,最后将 fsimage.ckpt 重命名为fsimage