Hadoop1.x:
Hadoop 的两大核心组件 HDFS 的NameNode 和 JobTracker 都存在着单点问题。
Hadoop2.x :
HDFS的NameNode 和 YARN的ResourceManger 的单点问题可以解决。
SecondaryNameNode保存的状态总是滞后于NameNode,所以这种方式难免会导致丢失部分数据,也可以解决。(NameNode和ResourceManger实现方式类似,前者更复杂)
下面主要讲HDFS NameNode的高可用(下图为HDFS NameNode 高可用架构图):
概念
Active NameNode 和 Standby NameNode:
两台 NameNode 形成互备,一台处于 Active 状态,为主 NameNode,另外一台处于 Standby 状态,为备 NameNode,只有主 NameNode 才能对外提供读写服务。
Active NN负责集群中所有客户端的操作;
Standby NN主要用于备用,它主要维持足够的状态,如果必要,可以提供快速
的故障恢复。
FailoverController(主备切换器):
FailoverController 作为独立的进程运行,对 NameNode 的主备切换进行总体控制。FailoverController 监测 NameNode 的健康状况,在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换。
HealthMonitor :
HealthMonitor是一个周期性工作的后台线程,它在一行个循环中周期性的同HA服务进心跳,负责跟踪NameNode服务的健康状况,并在健康状况变化时调用failover控制器的回调方法。
HealthMonitor 主要负责检测 NameNode 的健康状态,如果检测到 NameNode 的状态发生变化,会回调 ZKFailoverController 的相应方法进行自动的主备选举。
ActiveStandbyElector
ActiveStandbyElector 主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑,一旦 Zookeeper 主备选举完成,会回调 ZKFailoverController 的相应方法来进行 NameNode 的主备状态切换。
共享存储系统:
共享存储系统是实现 NameNode 的高可用最为关键的部分,共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和备NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。
使用JN(journalnode JQM方式):
JIN.pngQurom Journal Manager,基于Paxos(基于消息传递的一致性算法)。Paxos算法是解决分布式环境中如何就某个值达成一致(一个典型的场景是,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个"一致性算法"以保证每个节点看到的指令一致)。
实现方式:
(1) 初始化后,Active把editlog日志写到2N+1上JN上,每个editlog有一个编号,每次写editlog只要其中大多数JN返回成功(即大于等于N+1)即认定写成功。
(2) Standby定期从JN读取一批editlog,并应用到内存中的FsImage中。
(3) 如何fencing: NameNode每次写Editlog都需要传递一个编号Epoch给JN,JN会对比Epoch,如果比自己保存的Epoch大或相同,则可以写,JN更新自己的Epoch到最新,否则拒绝操作。在切换时,Standby转换为Active时,会把Epoch+1,这样就防止即使之前的NameNode向JN写日志,也会失败。
(4) 写日志:
(a) NN通过RPC向N个JN异步写Editlog,当有N/2+1个写成功,则本次写成功。
(b) 写失败的JN下次不再写,直到调用滚动日志操作,若此时JN恢复正常,则继续向其写日志。
(c) 每条editlog都有一个编号txid,NN写日志要保证txid是连续的,JN在接收写日志时,会检查txid是否与上次连续,否则写失败。
(5) 读日志:
(a) 定期遍历所有JN,获取未消化的editlog,按照txid排序。
(b) 根据txid消化editlog。
(6) 切换时日志恢复机制
(a) 主从切换时触发
(b) 准备恢复(prepareRecovery),standby向JN发送RPC请求,获取txid信息,并对选出最好的JN。
(c) 接受恢复(acceptRecovery),standby向JN发送RPC,JN之间同步Editlog日志。
(d) Finalized日志。即关闭当前editlog输出流时或滚动日志时的操作。
(e) Standby同步editlog到最新
(7) 如何选取最好的JN
(a) 有Finalized的不用in-progress
(b) 多个Finalized的需要判断txid是否相等
(c) 没有Finalized的首先看谁的epoch更大
(d) Epoch一样则选txid大的。
DataNode 节点:
共享 HDFS 的元数据信息以及共享HDFS 的数据块和 DataNode 之间的映射关系。DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。
NameNode主备切换过程
NameNode 主备切换主要由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现:
ZKFailoverController 作为 NameNode 机器上一个独立的进程启动 (在 hdfs 启动脚本之中的进程名为 zkfc),启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两个主要的内部组件,ZKFailoverController 在创建 HealthMonitor 和 ActiveStandbyElector 的同时,也会向 HealthMonitor 和 ActiveStandbyElector 注册相应的回调方法。
NameNode 实现主备切换的流程如图所示,有以下几步:
NameNode 主备过程.pngHealthMonitor 初始化完成后会启动内部的线程来定时调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法,对 NameNode 的健康状态进行检测。
HealthMonitor 如果检测到 NameNode 的健康状态发生变化,会回调 ZKFailoverController 注册的相应方法进行处理。
如果 ZKFailoverController 判断需要进行主备切换,会首先使用ActiveStandbyElector 来进行自动的主备选举。
ActiveStandbyElector 与 Zookeeper 进行交互完成自动的主备选举。
ActiveStandbyElector 在主备选举完成后,会回调 ZKFailoverController 的相应方法来通知当前的 NameNode 成为主 NameNode 或备 NameNode。
ZKFailoverController 调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法将 NameNode 转换为 Active 状态或 Standby 状态。
脑裂(split-brain)
脑裂(split-brain),指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。
脑裂问题处理:
共享存储的fencing,确保只有一个NN能写成功。使用QJM实现fencing,下文叙述原理。
DataNode的fencing,确保只有一个NN能命令DN。HDFS-1972中详细描述了DN如何实现fencing。
(1) 每个NN改变状态的时候,向DN发送自己的状态和一个序列号。
(2) DN在运行过程中维护此序列号,当主备切换时,新的NN在返回DN心跳时会返回自己的active状态和一个更大的序列号。DN接收到这个返回是认为该NN为新的active。
(3) 如果这时原来的active(比如GC)恢复,返回给DN的心跳信息包含active状态和原来的序列号,这时DN就会拒绝这个NN的命令。
此外HDFS-1972中还解决了一些有可能导致误删除block的隐患,在failover后,active在DN汇报所有删除报告前不应该删除任何block。
客户端fencing,确保只有一个NN能响应客户端请求。让访问standby NN的客户端直接失败。在RPC层封装了一层,通过FailoverProxyProvider以重试的方式连接NN。通过若干次连接一个NN失败后尝试连接新的NN,对客户端的影响是重试的时候增加一定的延迟。客户端可以设置重试此时和时间。
网友评论