HDFS——HDFS+Zookeeper搭建高可用HDFS

问题说明

NameNode是HDFS集群的单点故障点,每个集群只有一个NameNode,若是这个机器或进程不可用,整个集群就没法使用。为解决这一问题提供了两种解决方法:NFS(采用网络共享文件模式)和QJM(HDFS使用Quorum Journal Manager来共享Action NameNode与Standby NameNode之间的edit logs)。node

图 HDFS+Zookeeper实现高可用架构算法

总体架构

在一个HA集群中,会配置两个独立的NameNode。在任意时刻,只有一个NameNode做为活动的节点,另外一个节点则处于备份状态。活动的NameNode负责执行全部修改命名空间以及删除备份数据块的操做,而备份的NameNode则执行同步操做,以保持与活动节点命名空间的一致性。 promise

为了使备份节点与活动节点的状态可以同步一致,两个节点都须要同一组独立运行的节点(JournalNodes,JNS)通讯。当Active NameNode执行了修改命名空间的操做时,它会按期将执行的操做记录保存在在editlog中,并写入JNS的多数节点中。而Standby NameNode会一直监听JNS上editlog的变化,若是发现editlog有改动,Standby NameNode就会读取editlog并与当前的命名空间合并。当发生了错误切换时,Standby节点会保证已经从JNS上读取了全部editlog并与命名空间合并,而后才会从Standby状态切换为Active状态。经过这种机制,保证了Active NameNode与Standby NameNode之间命名空间状态的一致性,也就是第一关系链的一致性。 缓存

为了使错误切换可以很快的执行完毕,就要保证Standby节点也保存了实时的block的存储信息,也就是第二关系链。这样发生错误切换时,Standby节点就不须要等待全部的数据节点进行全量数据块汇报,而直接能够切换到Active状态。为了实现这个机制,DataNode会同时向这两个NameNode发送心跳以及块汇报信息。这样就实现了Active NameNode 和standby NameNode 的元数据就彻底一致,一旦发生故障,就能够立刻切换,也就是热备。网络

这里须要注意的是 Standby NameNode只会更新数据块的存储信息,并不会向NameNode 发送复制或者删除数据块的指令,这些指令只能由Active NameNode发送。 架构

在HA架构中有一个很是重非要的问题,就是须要保证同一时刻只有一个处于Active状态的NameNode,不然机会出现两个NameNode同时修改命名空间的问题,也就是脑裂(Split-brain)。脑裂的HDFS集群极可能形成数据块的丢失,以及向DataNode下发错误的指令等异常状况。为了预防脑裂的状况,HDFS提供了三个级别的隔离机制(fencing): 分布式

  1. 共享存储隔离:同一时间只容许一个NameNode向JournalNodes写入editlog数据。 
  2. 客户端隔离:同一时间只容许一个NameNode响应客户端的请求。 
  3. Datanode隔离:同一时间只容许一个NameNode向DataNode下发名字节点指令,例如删除、复制数据块指令等等。

在HA实现中还有一个很是重要的部分就是Active NameNode和Standby NameNode之间如何共享editlog日志文件。Active NameNode会将日志文件写到共享存储上。Standby NameNode会实时的从共享存储读取editlog文件,而后合并到Standby NameNode的命名空间中。这样一旦Active NameNode发生错误,Standby NameNode能够当即切换到Active状态。在Hadoop2.6中,提供了QJM(Quorum Journal Manager)方案来解决HA共享存储问题。oop

全部的HA实现方案都依赖于一个保存editlog的共享存储,这个存储必须是高可用的,而且可以被集群中全部的NameNode同时访问。Quorum Journal是一个基于paxos算法的HA设计方案。 性能

Quorum Journal

Quorum Journal方案中有两个重要的组件。 url

  1. JournalNoe(JN):运行在N台独立的物理机器上,它将editlog文件保存在JournalNode的本地磁盘上,同时JournalNode还对外提供RPC接口QJournalProtocol以执行远程读写editlog文件的功能。 
  2. Quorum Journal Manager(QJM):运行在NameNode上,(目前HA集群只有两个NameNode),经过调用RPC接口QJournalProtocol中的方法向JournalNode发送写入、排斥、同步editlog。

Quorum Journal方案依赖于这样一个概念:HDFS集群中有2N+1个JN存储editlog文件,这些editlog 文件是保存在JN的本地磁盘上的。每一个JN对QJM暴露QJM接口QJournalProtocol,容许NameNode读写editlog文件。当NameNode向共享存储写入editlog文件时,它会经过QJM向集群中全部的JN发送写editlog文件请求,当有一半以上的JN返回写操做成功时,即认为写成功。这个原理是基于Paxos算法的。

使用Quorum Journal实现的HA方案有一下优势: 

  1. JN进程能够运行在普通的PC上,而无需配置专业的共享存储硬件。 
  2. 不须要单独实现fencing机制,Quorum Journal模式中内置了fencing功能。 
  3. Quorum Journal不存在单点故障,集群中有2N+1个Journal,能够容许有N个Journal Node死亡。 
  4. JN不会由于其中一个机器的延迟而影响总体的延迟,并且也不会由于JN数量的增多而影响性能(由于NameNode向JournalNode发送日志是并行的)

互斥机制

当HA集群中发生NameNode异常切换时,须要在共享存储上fencing上一个活动的节点以保证该节点不能再向共享存储写入editlog。基于Quorum Journal模式的HA提供了epoch number来解决互斥问题,这个概念能够在分布式文件系统中找到。epoch number具备如下几个性质。 
1.当一个NameNode变为活动状态时,会分配给他一个epoch number。 
2.每一个epoch number都是惟一的,没有任意两个NameNode有相同的epoch number。 
3.epoch number 定义了NameNode写editlog文件的顺序。对于任意两个NameNode ,拥有更大epoch number的NameNode被认为是活动节点。

当一个NameNode切换为活动状态时,它的QJM会向全部的JN发送命令,以获取该JN的最后一个promise epoch变量值。当QJM接受到了集群中多于一半的JN回复后,它会将所接收到的最大值加一,并保存到myepoch 中,以后QJM会将该值发送给全部的JN并提出更新请求。每一个JN会将该值与自身的epoch值相互比较,若是新的myepoch比较大,则JN更新,并返回更新成功;若是小,则返回更新失败。若是QJM接收到超过一半的JN返回成功,则设置它的epoch number为myepoch;不然它终止尝试为一个活动的NameNode,并抛出异常。

当活动的NameNode成功获取并更新了epoch number后,调用任何修改editlog的RPC请求都必须携带epoch number。当RPC请求到达JN后,JN会将请求者的epoch与自身保存的epoch相互对比,若请求者的epoch更大,JN就会更新本身的epoch,并执行相应的操做,若是请求者的epoch小,就会拒绝相应的请求。当集群中大多数的JN拒绝了请求时,此次操做就失败了。 
当HDFS集群发生NameNode错误切换后,原来的standby NameNode将集群的epoch number加一后更新。这样原来的Active NameNode的epoch number确定小于这个值,当这个节点执行写editlog操做时,因为JN节点不接收epoch number小于自身的promise epoch的写请求,因此此次写请求会失败,也就达到了fencing的目的。

editlog写流程 

  1. 将editlog输出流中缓存的数据写入JN,对于集群中的每个JN都存在一个独立的线程调用RPC 接口中的方法向JN写入数据。 
  2. 当JN收到请求以后,JN会执行如下操做: 
    1. 验证epoch number是否正确 
    2. 确认写入数据对应的txid是否连续 
    3. 将数据持久化到JN的本地磁盘 
    4. 向QJM发送正确的响应
  3. QJM等待集群JN的响应,若是多数JN返回成功,则写操做成功;不然写操做失败,QJM会抛出异常。
  • NameNode会调用FSEditlogLog下面的方法初始化editlog文件的输出流,而后使用输出流对象向editlog文件写入数据。 
  • 获取了QuorumOutputStream输出流对象以后,NameNode会调用write方法向editlog文件中写入数据,QuorumOutputStream的底层也调用了EditsDoubleBuffer双缓存区。数据回先写入其中一个缓冲区中,而后调用flush方法时,将缓冲区中的数据发送给JN。

读流程 

Standby NameNode会从JN读取editlog,而后与Sdtandby NameNode的命名空间合并,以保持和Active NameNode命名空间的同步。当Sdtandby NameNode从JN读取editlog时,它会首先发送RPC请求到集群中全部的JN上。JN接收到这个请求后会将JN本地存储上保存的全部FINALIZED状态的editlog段落文件信息返回,以后QJM会为全部JN返回的editlog段落文件构造输入流对象,并将这些输入流对象合并到一个新的输入流对象中,这样Standby namenode就能够从任一个JN读取每一个editlog段落了。若是其中一个JN失败了输入流对象会自动切换到另外一个保存了该edirlog段落的JN上。 

恢复流程 

当NameNode发生主从切换时,原来的Standby NameNode会接管共享存储并执行写editlog的操做。在切换以前,对于共享存储会执行如下操做: 

  1. fencing原来的Active NameNode。这部分在互斥部分已经讲述。 
  2. 恢复正在处理的editlog。因为NameNode发生了主从切换,集群中JN上正在执行写入操做的editlog数据可能不一致。例如,可能出现某些JN上的editlog正在写入,可是当前Active NameNode发生错误,这时该JN上的editlog文件就与已完成写入的JN不一致。在这种状况下,须要对JN上全部状态不一致的editlog文件执行恢复操做,将他们的数据同步一致,而且将editlog文件转化为FINALIZED状态。 
  3. 当不一致的editlog文件完成恢复以后,这时原来的Standby NameNode就能够切换为Active NameNode并执行写editlog的操做。 
  4. 写editlog。在前面已经介绍了。

日志恢复操做

日志恢复操做能够分为如下几个阶段: 

  1. 肯定须要执行恢复操做的editlog段落:在执行恢复操做以前,QJM会执行newEpoch()调用以产生新的epoch number,JN接收到这个请求后除了执行更新epoch number外,还会将该JN上保存的最新的editlog段落的txid返回。当集群中的大多数JN都发回了这个响应后,QJM就能够肯定出集群中最新的一个正在处理editlog段落的txid,而后QJM就会对这个txid对应的editlog段落执行恢复操做了。
  2. 准备恢复:QJM向集群中的全部JN发送RPC请求,查询执行恢复操做的editlog段落文件在全部JN上的状态,这里的状态包括editlog文件是in-propress仍是FINALIZED状态,以及editlog文件的长度。
  3. 接受恢复:QJM接收到JN发回的JN发回的响应后,会根据恢复算法选择执行恢复操做的源节点。而后QJM会发送RPC请求给每个JM,这个请求会包含两部分信息:源editlog段落文件信息,以及供JN下载这个源editlog段落的url。 
  4. 接收到这个RPC请求以后,JN会执行如下操做: 
    1. 同步editlog段落文件,若是JN磁盘上的editlog段落文件与请求中的段落文件状态不一样,则JN会从当前请求中的url上下载段落文件,并替换磁盘上的editlog段落文件。 
    2. 持久化恢复元数据,JN会将执行恢复操做的editlog段落文件的状态、触发恢复操做的QJM的epoch number等信息(恢复的元数据信息)持久化到磁盘上。 
    3. 当这些操做都执行成功后,JN会返回成功响应给QJM,若是集群中的大多数JN都返回了成功,则这次恢复操做执行成功。
  5. 完成editlog段落文件:到这步操做时,QJM 就能肯定集群中大多数的JN保存的editlog文件的状态已经一致了,而且JN持久化了恢复信息。QJM就会向JN发送指令,将这个editlog段落文件的状态转化为FINALIZED状态,,而且JN会删除持久化的恢复元数据,由于磁盘上保存的editlog文件信息已是正确的了,不须要保存恢复的元数据。

HDFS的高可用机制详解       

相关文章
相关标签/搜索