一文弄懂HDFS的Ha高可用原理

文章目录

  • 一、Hadoop1.x中hdfs架构
  • 二、Hadoop2.x中hdfs架构
  • 三、Hadoop 2.x元数据
  • 四、基于 QJM 的共享存储系统的总体架构
    • 1、基于 QJM 的共享存储系统的内部实现架构图如图。
    • 2、QJM 写过程分析
  • 五、HDFS的HA参数配置
  • 六、hdfs中的DataNode节点配置

一、Hadoop1.x中hdfs架构

在介绍HA之前,我们先来看下Hadoop的系统架构,这对于理解HA是至关重要的。Hadoop 1.x之前,其官方架构如图1所示:
一文弄懂HDFS的Ha高可用原理_第1张图片

从图中可看出,1.x版本之前只有一个Namenode,所有元数据由惟一的Namenode负责管理,可想而之当这个NameNode挂掉时整个集群基本也就不可用。
Hadoop 2.x的架构与1.x有什么区别呢。我们来看下2.x的架构。

二、Hadoop2.x中hdfs架构

一文弄懂HDFS的Ha高可用原理_第2张图片
2.x版本中,HDFS架构解决了单点故障问题,即引入双NameNode架构,同时借助共享存储系统来进行元数据的同步,共享存储系统类型一般有几类,如:Shared NAS+NFS、BookKeeper、BackupNode 和 Quorum Journal Manager(QJM),上图中用的是QJM作为共享存储组件,通过搭建奇数结点的JournalNode实现主备NameNode元数据操作信息同步。Hadoop的元数据包括哪些信息呢,下面介绍下关于元数据方面的知识。

三、Hadoop 2.x元数据

Hadoop的元数据主要作用是维护HDFS文件系统中文件和目录相关信息。元数据的存储形式主要有3类:内存镜像、磁盘镜像(FSImage)、日志(EditLog)。 在Namenode启动时,会加载磁盘镜像到内存中以进行元数据的管理,存储在NameNode内存;磁盘镜像是某一时刻HDFS的元数据信息的快照,包含所有相关Datanode节点文件块映射关系和命名空间(Namespace)信息,存储在NameNode本地文件系统;日志文件记录client发起的每一次操作信息,即保存所有对文件系统的修改操作,用于定期和磁盘镜像合并成最新镜像,保证NameNode元数据信息的完整,存储在NameNode本地和共享存储系统(QJM)中。
一个典型的 NameNode 的元数据存储目录结构如图 3 所示 (图片来源于参考文献 [4]),这里主要关注其中的 EditLog 文件和 FSImage 文件:
一文弄懂HDFS的Ha高可用原理_第3张图片
NameNode 在执行 HDFS 客户端提交的创建文件或者移动文件这样的写操作的时候,会首先把这些操作记录在 EditLog 文件之中,然后再更新内存中的文件系统镜像。内存中的文件系统镜像用于 NameNode 向客户端提供读服务,而 EditLog 仅仅只是在数据恢复的时候起作用。记录在 EditLog 之中的每一个操作又称为一个事务,每个事务有一个整数形式的事务 id 作为编号。EditLog 会被切割为很多段,每一段称为一个 Segment。正在写入的 EditLog Segment 处于 in-progress 状态,其文件名形如 edits_inprogress_ s t a r t t x i d , 其 中 {start_txid},其中 starttxid{start_txid} 表示这个 segment 的起始事务 id,例如上图中的 edits_inprogress_0000000000000000020。而已经写入完成的 EditLog Segment 处于 finalized 状态,其文件名形如 edits_ s t a r t t x i d − {start_txid}- starttxid{end_txid},其中 s t a r t t x i d 表 示 这 个 s e g m e n t 的 起 始 事 务 i d , {start_txid} 表示这个 segment 的起始事务 id, starttxidsegmentid{end_txid} 表示这个 segment 的结束事务 id,例如上图中的 edits_0000000000000000001-0000000000000000019。

NameNode 会定期对内存中的文件系统镜像进行 checkpoint 操作,在磁盘上生成 FSImage 文件,FSImage 文件的文件名形如 fsimage_ e n d t x i d , 其 中 {end_txid},其中 endtxid{end_txid} 表示这个 fsimage 文件的结束事务 id,例如上图中的 fsimage_0000000000000000020。在 NameNode 启动的时候会进行数据恢复,首先把 FSImage 文件加载到内存中形成文件系统镜像,然后再把 EditLog 之中 FsImage 的结束事务 id 之后的 EditLog 回放到这个文件系统镜像上。

HA其本质上就是要保证主备NN元数据是保持一致的,即保证fsimage和editlog在备NN上也是完整的。元数据的同步很大程度取决于EditLog的同步,而这步骤的关键就是共享文件系统,

四、基于 QJM 的共享存储系统的总体架构

基于 QJM(Quorum Journal Manager) 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。QJM 共享存储的基本思想来自于 Paxos 算法 (参见参考文献 [3]),采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉。

1、基于 QJM 的共享存储系统的内部实现架构图如图。

一文弄懂HDFS的Ha高可用原理_第4张图片
主要包含下面几个主要的组件:

  • FSEditLog:这个类封装了对 EditLog 的所有操作,是 NameNode 对 EditLog 的所有操作的入口。
  • JournalSet: 这个类封装了对本地磁盘和 JournalNode 集群上的 EditLog 的操作,内部包含了两类 JournalManager,一类为 FileJournalManager,用于实现对本地磁盘上 EditLog 的操作。一类为 QuorumJournalManager,用于实现对 JournalNode 集群上共享目录的 EditLog 的操作。FSEditLog 只会调用 JournalSet 的相关方法,而不会直接使用 FileJournalManager 和 QuorumJournalManager。
  • FileJournalManager:封装了对本地磁盘上的 EditLog 文件的操作,不仅 NameNode 在向本地磁盘上写入 EditLog 的时候使用 FileJournalManager,JournalNode 在向本地磁盘写入 EditLog 的时候也复用了 FileJournalManager 的代码和逻辑。
  • QuorumJournalManager:封装了对 JournalNode 集群上的 EditLog 的操作,它会根据 JournalNode 集群的 URI 创建负责与 JournalNode 集群通信的类 AsyncLoggerSet, QuorumJournalManager 通过 AsyncLoggerSet 来实现对 JournalNode 集群上的 EditLog 的写操作,对于读操作,QuorumJournalManager 则是通过 Http 接口从 JournalNode 上的 JournalNodeHttpServer 读取 EditLog 的数据。
  • AsyncLoggerSet:内部包含了与 JournalNode 集群进行通信的 AsyncLogger 列表,每一个 AsyncLogger 对应于一个 JournalNode 节点,另外 AsyncLoggerSet 也包含了用于等待大多数 JournalNode 返回结果的工具类方法给 QuorumJournalManager 使用。
  • AsyncLogger:具体的实现类是 IPCLoggerChannel,IPCLoggerChannel 在执行方法调用的时候,会把调用提交到一个单线程的线程池之中,由线程池线程来负责向对应的 JournalNode 的 JournalNodeRpcServer 发送 RPC 请求。
  • JournalNodeRpcServer:运行在 JournalNode 节点进程中的 RPC 服务,接收 NameNode 端的 AsyncLogger 的 RPC 请求。
  • JournalNodeHttpServer:运行在 JournalNode 节点进程中的 Http 服务,用于接收处于 Standby 状态的 NameNode 和其它 JournalNode 的同步 EditLog 文件流的请求。

2、QJM 写过程分析

上面提到EditLog,NameNode会把EditLog同时写到本地和JournalNode。写本地由配置中参数dfs.namenode.name.dir控制,写JN由参数dfs.namenode.shared.edits.dir控制,在写EditLog时会由两个不同的输出流来控制日志的写过程,分别为:EditLogFileOutputStream(本地输出流)和QuorumOutputStream(JN输出流)。写EditLog也不是直接写到磁盘中,为保证高吞吐,NameNode会分别为EditLogFileOutputStream和QuorumOutputStream定义两个同等大小的Buffer,大小大概是512KB,一个写Buffer(buffCurrent),一个同步Buffer(buffReady),这样可以一边写一边同步,所以EditLog是一个异步写过程,同时也是一个批量同步的过程,避免每写一笔就同步一次日志。
这个是怎么实现边写边同步的呢,这中间其实是有一个缓冲区交换的过程,即bufferCurrent和buffReady在达到条件时会触发交换,如bufferCurrent在达到阈值同时bufferReady的数据又同步完时,bufferReady数据会清空,同时会将bufferCurrent指针指向bufferReady以满足继续写,另外会将bufferReady指针指向bufferCurrent以提供继续同步EditLog。上面过程用流程图就是表示如下:
一文弄懂HDFS的Ha高可用原理_第5张图片
这里有一个问题,既然EditLog是异步写的,怎么保证缓存中的数据不丢 呢,其实这里虽然是异步,但实际所有日志都需要通过logSync同步成功后才会给client返回成功码,假设某一时刻NameNode不可用了,其内存中的数据其实是未同步成功的,所以client会认为这部分数据未写成功。
第二个问题是,EditLog怎么在多个JN上保持一致的呢。下面展开介绍。
1.隔离双写:
在ANN每次同步EditLog到JN时,先要保证不会有两个NN同时向JN同步日志。这个隔离是怎么做的。这里面涉及一个很重要的概念Epoch Numbers,很多分布式系统都会用到。Epoch有如下几个特性:

  • 当NN成为活动结点时,其会被赋予一个EpochNumber
  • 每个EpochNumber是惟一的,不会有相同的EpochNumber出现
  • EpochNumber有严格顺序保证,每次NN切换后其EpochNumber都会自增1,后面生成的EpochNumber都会大于前面的EpochNumber

QJM是怎么保证上面特性的呢,主要有以下几点:

  • 第一步,在对EditLog作任何修改前,QuorumJournalManager(NameNode上)必须被赋予一个EpochNumber
  • 第二步, QJM把自己的EpochNumber通过newEpoch(N)的方式发送给所有JN结点
  • 第三步, 当JN收到newEpoch请求后,会把QJM的EpochNumber保存到一个lastPromisedEpoch变量中并持久化到本地磁盘
  • 第四步, ANN同步日志到JN的任何RPC请求(如logEdits(),startLogSegment()等),都必须包含ANN的EpochNumber
  • 第五步,JN在收到RPC请求后,会将之与lastPromisedEpoch对比,如果请求的EpochNumber小于lastPromisedEpoch,将会拒绝同步请求,反之,会接受同步请求并将请求的EpochNumber保存在lastPromisedEpoch

这样就能保证主备NN发生切换时,就算同时向JN同步日志,也能保证日志不会写乱,因为发生切换后,原ANN的EpochNumber肯定是小于新ANN的EpochNumber,所以原ANN向JN的发起的所有同步请求都会拒绝,实现隔离功能,防止了脑裂。

2. 恢复in-process日志
为什么要这步呢,如果在写过程中写失败了,可能各个JN上的EditLog的长度都不一样,需要在开始写之前将不一致的部分恢复。恢复机制如下:

  • 1 ANN先向所有JN发送getJournalState请求;
  • 2 JN会向ANN返回一个Epoch(lastPromisedEpoch);
  • 3 ANN收到大多数JN的Epoch后,选择最大的一个并加1作为当前新的Epoch,然后向JN发送新的newEpoch请求,把新的Epoch下发给JN;
  • 4 JN收到新的Epoch后,和lastPromisedEpoch对比,若更大则更新到本地并返回给ANN自己本地一个最新EditLogSegment起始事务Id,若小则返回NN错误;
  • 5 ANN收到多数JN成功响应后认为Epoch生成成功,开始准备日志恢复;
  • 6 ANN会选择一个最大的EditLogSegment事务ID作为恢复依据,然后向JN发送prepareRecovery; RPC请求,对应Paxos协议2p阶段的Phase1a,若多数JN响应prepareRecovery成功,则可认为Phase1a阶段成功;
  • 7 ANN选择进行同步的数据源,向JN发送acceptRecovery RPC请求,并将数据源作为参数传给JN。
  • 8 JN收到acceptRecovery请求后,会从JournalNodeHttpServer下载EditLogSegment并替换到本地保存的EditLogSegment,对应Paxos协议2p阶段的Phase1b,完成后返回ANN请求成功状态。
  • 9 ANN收到多数JN的响应成功请求后,向JN发送finalizeLogSegment请求,表示数据恢复完成,这样之后所有JN上的日志就能保持一致。

数据恢复后,ANN上会将本地处于in-process状态的日志更名为finalized状态的日志,形式如edits[start-txid][stop-txid]。

3.日志同步
这个步骤上面有介绍到关于日志从ANN同步到JN的过程,具体如下:

  • 1 执行logSync过程,将ANN上的日志数据放到缓存队列中
  • 2 将缓存中数据同步到JN,JN有相应线程来处理logEdits请求
  • 3 JN收到数据后,先确认EpochNumber是否合法,再验证日志事务ID是否正常,将日志刷到磁盘,返回ANN成功码
  • 4 ANN收到JN成功请求后返回client写成功标识,若失败则抛出异常

通过上面一些步骤,日志能保证成功同步到JN,同时保证JN日志的一致性,进而备NN上同步日志时也能保证数据是完整和一致的。

五、HDFS的HA参数配置

可以参照官网配置:http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html
安装示例:
https://www.cnblogs.com/raphael5200/p/5154325.html
https://www.jianshu.com/p/e589c7fe5a52
一文弄懂HDFS的Ha高可用原理_第6张图片

注:

  • NameNode 机器:推荐主备 NameNode 具有相同的硬件配置,且内存要足够大。
  • JournalNode:通常准备 3 或 5 个 JournalNode,考虑到 JournalNode 非常轻量级,可以与 Hadoop 其他服务共用机器,比如 ResourceManager,TaskTracker 等。
  • Zookeeper:由于 Hadoop 多个服务用到了 Zookeeper,可搭建一个 3 或者 5 个节点的Zookeeper 实例作为公共服务。Zookeeper 实例也可以与其他服务共用机器。

六、hdfs中的DataNode节点配置

slaves文件里面记录的是集群里所有DataNode的主机名,到底它是怎么作用的呢?slaves文件只在NameNode上生效,代表集群的datanode节点 ,例如示例slaves

host1
host2
host3 

在hdfs-site.xml中配置,分别是允许slave连接Namenode的列表和拒绝连接的列表。


    dfs.hosts
    /software/servers/hadoop-2.7.1/etc/hadoop/hosts/slaves


    dfs.hosts.exclude
    /software/servers/hadoop-2.7.1/etc/hadoop/hosts/slaves_exclude
 

扩展:
hadoop client节点其实质为一台特殊的DataNode,其只用于提交任务,而不参与数据存储和计算,其主要实质和datanode节点配置一致,只是在NameNode的slaves文件没有配置该节点而已。

参考:
Hadoop NameNode 高可用 (High Availability) 实现解析:https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-name-node/
深入理解Hadoop HA机制:http://www.aboutyun.com/thread-22935-1-1.html
Hadoop常用参数整理(HDFS/Yarn/MapReduce/GC):http://www.zhangrenhua.com/2016/01/05/hadoop-%E5%B8%B8%E7%94%A8%E5%8F%82%E6%95%B0%E6%95%B4%E7%90%86/
Hadoop slaves文件解析:http://www.firefoxbug.com/index.php/archives/2401
hadoop client机搭建:https://coderlxl201209164551.iteye.com/blog/2420224

你可能感兴趣的:(一文弄懂系列,大数据)