一、NameNode高可用整体架构
架构:
NameNode组建包括:
(1)Active NameNode——主NameNode,可以对外提供读写服务
(2)standby NameNode——备NameNode
(3)ZKFailoverController——主备切换控制器,独立进程运行,可及时检测到NameNode的健康状况,不依赖与手动切换
(4)Zookeeper集群——为主备切换控制器提供主备选举支持
(5)共享存储系统——是实现NameNode高可用性的关键,对元数据(并不是所有)进行共享和存储,而主NameNode和备NameNode通过其实现数据的同步。
(6)DataNode——同时向主NameNode和备NameNode 上报数据块的位置信息
二、NameNode的主备切换
流程图:
NameNode主备切换主要是由zkfc完成,主要由HealthMonitor、ZKFailoverController和ActiveStandbyElector三个组件协同完成。
zkfc作为独立进程启动,启动时创建HealthMonitor和ActiveStandbyElector两个主要内部组件。
HealthMonitor主要负责检测NameNode 的健康状态,如果检查到NameNode的健康状态发生改变,它会回调ZKFailoverController的相应方法进行自动的主备选举。
ActiveStandbyElector主要负责完成自动的主备选举,它内部封装了Zookeeper的处理逻辑,一旦主备选举完成,就会回调ZKFailoverController的相应方法进行主备切换。
ZKFC监控Active NameNode健康状况,一旦发生问题,利用Zookeeper 集群进行选举。再决定NameNode到底是Active NameNode还是standby NameNode。
三、NameNode的共享存储
NameNode 在执行 HDFS 客户端提交的创建文件或者移动文件这样的写操作的时候,会首先把这些操作记录在 EditLog 文件之中,然后再更新内存中的文件系统镜像。内存中的文件系统镜像用于 NameNode 向客户端提供读服务,而 EditLog 仅仅只是在数据恢复的时候起作用。
NameNode 会定期对内存中的文件系统镜像进行 checkpoint 操作,在磁盘上生成 FSImage 文件。在 NameNode 启动的时候会进行数据恢复,首先把 FSImage 文件加载到内存中形成文件系统镜像,然后再把 EditLog 之中 FsImage 的结束事务 id 之后的 EditLog 回放到这个文件系统镜像上。
基于 QJM 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉。
当处于 Active 状态的 NameNode 调用 FSEditLog 类的 logSync 方法来提交 EditLog 的时候,会通过 JouranlSet 同时向本地磁盘目录和 JournalNode 集群上的共享存储目录写入 EditLog。写入 JournalNode 集群是通过并行调用每一个 JournalNode 的 QJournalProtocol RPC 接口的 journal 方法实现的,如果对大多数 JournalNode 的 journal 方法调用成功,那么就认为提交 EditLog 成功,否则 NameNode 就会认为这次提交 EditLog 失败。
当 NameNode 进入 Standby 状态之后,会启动一个 EditLogTailer 线程。这个线程会定期调用 EditLogTailer 类的 doTailEdits 方法从 JournalNode 集群上同步 EditLog,然后把同步的 EditLog 回放到内存之中的文件系虽然 Active NameNode 向 JournalNode 集群提交 EditLog 是同步的,但 Standby NameNode 采用的是定时从 JournalNode 集群上同步 EditLog 的方式,那么 Standby NameNode 内存中文件系统镜像有很大的可能是落后于 Active NameNode 的,所以 Standby NameNode 在转换为 Active NameNode 的时候需要把落后的 EditLog 补上来。
处于 Standby 状态的 NameNode 转换为 Active 状态的时候,有可能上一个 Active NameNode 发生了异常退出,那么 JournalNode 集群中各个 JournalNode 上的 EditLog 就可能会处于不一致的状态,所以首先要做的事情就是让 JournalNode 集群中各个节点上的 EditLog 恢复为一致。另外,当前处于 Standby 状态的 NameNode 的内存中的文件系统镜像有很大的可能是落后于旧的 Active NameNode 的,所以在 JournalNode 集群中各个节点上的 EditLog 达成一致之后,接下来要做的事情就是从 JournalNode 集群上补齐落后的 EditLog。只有在这两步完成之后,当前新的 Active NameNode 才能安全地对外提供服务。