当前位置: 首页 > 科技观察

学习Hadoop技术的HA机制

时间:2023-03-14 20:29:07 科技观察

介绍最近分享了一个主题为Hadoop技术的演讲。由于接触的时间短,很多技术细节都不是很了解,也没有解释清楚。作为一名技术人员,本着追根溯源的精神,还是有必要深入了解,为自己的工作积累一些经验总结的。网上大部分关于HadoopHA的资料都集中在如何构建HA上,很少有关于HA为什么要这样做的描述。因此本文不介绍HA是如何搭建的。主要介绍HA的工作原理和QJM的工作原理。.1.Hadoop系统架构1.1Hadoop1.x和Hadoop2.x架构在介绍HA之前,我们先看一下Hadoop系统架构,这对于理解HA至关重要。Hadoop1.x之前,其官方架构如图1所示:【图1.Hadoop1.x架构图】从图中可以看出,1.x之前的版本只有一个Namenode,所有元数据都在负责唯一的NamenodeManagement,可以想象,当NameNode挂掉后,整个集群基本不可用。Hadoop2.x架构和1.x有什么区别?我们来看看2.x架构:【图2.Hadoop2.x架构图】在2.x版本中,HDFS架构解决了单点故障问题,即引入了双NameNode架构,以及同时使用共享存储系统来进行数据同步,共享存储系统一般有以下几种,例如:SharedNAS+NFS、BookKeeper、BackupNode和QuorumJournalManager(QJM)。上图中QJM作为共享存储组件。通过构建一个节点数为奇数的JournalNode,同步主备NameNode的元数据操作信息。Hadoop的元数据包括哪些信息?下面介绍有关元数据的知识。1.2Hadoop2.x元数据Hadoop元数据的主要作用是维护HDFS文件系统中文件和目录的信息。元数据的存储形式主要有内存镜像、磁盘镜像(FSImage)和日志(EditLog)三种。Namenode启动时,会将磁盘镜像加载到内存中进行元数据管理,并存储在NameNode内存中;磁盘镜像是HDFS元数据信息在某一时刻的快照,包括所有与Datanode节点相关的文件块映射关系和命名空间(Namespace)信息,存储在NameNode本地文件系统中;日志文件记录了客户端发起的每一次操作信息,即保存所有对文件系统的修改操作,用于定期与磁盘镜像合并成一个***镜像,以保证NameNode元数据信息的完整性存储在NameNode本地和共享存储系统(QJM)中。下面展示了NameNode本地的EditLog和FSImage文件格式。EditLog文件有两种状态:inprocess和finalized,inprocess表示正在写入的日志文件,文件名格式:editsinprocess[start-txid],finalized表示已经写入的日志文件,文件名形式:edits[start-txid][结束-txid];FSImage文件也有两种状态,finalized和checkpoint,finalized表示一个文件已经持久化在磁盘上,文件名形式:fsimage_[end-txid],checkpoint表示正在合并的fsimage。2.x版本的checkpoint流程是在StandbyNamenode(SNN)上进行的。SNN会周期性合并本地的FSImage和从QJM拉回来的ANN的EditLog,合并后通过RPC传回给ANN。data/hbase/runtime/namespace├──current│├──VERSION│├──edits_0000000003619794209-0000000003619813881│├──edits_0000000003619813882-0000000003619831665│├──edits_0000000003619831666-0000000003619852153│├──edits_0000000003619852154-0000000003619871027│├──edits_0000000003619871028-0000000003619880765│├──edits_0000000003619880766-0000000003620060869│├──edits_inprogress_0000000003620060870│├──fsimage_0000000003618370058│├──fsimage_0000000003618370058.md5│├──fsimage_0000000003620060869│├──fsimage_0000000003620060869.md5│└──seen_txid└──in_use.lock上面所显示的另一个非常重要的文件是seen_txid,它存储交易ID。此事务ID是EditLog***的结束事务ID。当NameNode重启时,会依次遍历从edits_0000000000000000001到seen_txid记录的txid,如果日志文件丢失或者记录的事务ID有问题,数据块信息就会丢失。HA的本质是保证主备NN的元数据一致,即保证fsimage和editlog在备NN上也是完整的。元数据的同步很大程度上依赖于EditLog的同步,而这一步的关键是共享文件系统。先介绍一下QJM的共享存储机制。2.QJM原理2.1QJM产生背景在QJM出现之前,为了保证集群的HA,设计了一种基于NAS的共享存储机制,即通过NAS在主备NameNode之间进行元数据同步。这个解决方案有什么缺点?要点如下:定制硬件设备:必须是支持NAS的设备才符合要求。部署容易出错,简单化NFS客户端:BUG较多,部署配置容易出错,导致HA不可用。因此,对于替代方案,也必须解决NAS相关的缺陷,才能让HA更好地服务。即设备无需定制,普通设备即可配置HA。部署简单,相关配置集成到系统本身,无需定制。同时元数据的同步要保证完全HA,不会因为客户端问题导致同步失败。2.2QJM原理2.2.1QJM简介QJM全称QuorumJournalManager,由JournalNodes(JN)组成,通常为奇数节点。每个JournalNode都有一个简单的RPC接口供NameNode读写EditLog到JN本地磁盘。在写入EditLog时,NameNode会同时并行的向所有JournalNodes写入文件。只要N/2+1个节点写入成功,就认为写入操作成功,遵循Paxos协议。其内部实现框架如下:【图3.QJM内部实现框架】从图中可以看出,主要涉及到EditLog的不同管理对象和输出流对象,每个对象所起的作用不同:FSEditLog:所有EditLog操作入口JournalSet:集成本地磁盘及JournalNode集群EditLog的相关操作FileJournalManager:实现本地磁盘的EditLog操作QuorumJournalManager:实现JournalNode集群的EditLog操作AsyncLoggerSet:实现JournalNode集群EditLogAsyncLogger的写操作集:向JN发起RPC请求,执行特定的Log同步功能JournalNodeRpcServer:运行在JournalNode节点进程中的RPC服务,接收来自NameNode端AsyncLogger的RPC请求。JournalNodeHttpServer:运行在JournalNode节点进程中的Http服务,用于接收来自处于Standby状态的NameNode和其他JournalNodes对同步EditLog文件流的请求。下面详细分析QJM的读写过程。2.2.2QJM写入过程分析上面提到EditLog,NameNode会将EditLog同时写入本地和JournalNode。本地写入由配置中的参数dfs.namenode.name.dir控制,写入JN由参数dfs.namenode.shared.edits.dir控制。在写EditLog时,两个不同的输出流会分别控制日志的写入过程。对于:EditLogFileOutputStream(本地输出流)和QuorumOutputStream(JN输出流)。写入EditLog并不是直接写入磁盘。为了保证高吞吐量,NameNode会分别为EditLogFileOutputStream和QuorumOutputStream定义两个大小相同的Buffer。大小约为512KB。可以边写边同步,所以EditLog是一个异步写入过程,也是一个批量同步过程,避免了每写一行就同步日志。这如何在写入时实现同步?中间其实有一个buffer交换的过程,就是bufferCurrent和buffReady满足条件的时候会触发交换。例如,当bufferCurrent达到阈值,同步bufferReady数据时,bufferReady数据会被清除,bufferCurrent指针指向bufferReady继续写入,bufferReady指针指向bufferCurrent继续同步EditLog。上述过程的流程图如下:【图4.EditLog输出流程图】这里有个问题。既然EditLog是异步写入的,那么如何保证缓存中的数据不丢失呢?其实这里虽然是异步的,但是所有实际的日志,只有通过logSync同步成功后,才会返回给客户端。假设某个时刻NameNode不可用,其内存中的数据没有同步成功,那么client就会认为这部分数据没有写入成功。第二个问题,EditLog如何在多个JN上保持一致?以下是介绍。1、隔离双写:ANN每次向JN同步EditLog时,必须保证不会有两个NN同时向JN同步日志。这种隔离是如何完成的。这就涉及到一个非常重要的概念,EpochNumbers,在很多分布式系统中都会用到。Epoch具有以下特点:当NN成为活跃节点时,它会被赋予一个EpochNumber。每个EpochNumber都是唯一的,不会出现相同的EpochNumber。加1,后面生成的EpochNumber会大于前面的EpochNumber。QJM是如何保证以上特性的,主要是:首先,在对EditLog进行任何修改之前,必须给QuorumJournalManager(在NameNode上)分配一个EpochNumberStep2,QJM通过newEpoch(N)向所有JN节点发送自己的EpochNumber).Step3,当JN收到newEpoch请求时,会将QJM的EpochNumber保存到一个lastPromisedEpoch变量中,持久化到本地磁盘Step4,ANN向JN发送任何RPC请求(如logEdits(),startLogSegment()等)必须包含ANN的EpochNumber。Step5,JN收到RPC请求后,会与lastPromisedEpoch进行比较,如果请求的EpochNumber小于lastPromisedEpoch,则拒绝同步请求。否则,将接受同步请求,并将请求的EpochNumber保存在lastPromisedEpoch中。这样可以保证主备NN切换时,即使同时向JN同步了日志,也不会丢失日志。会很乱,因为切换后,原ANN的EpochNumber必须小于新ANN的EpochNumber,所以原ANN向JN发起的所有同步请求都会被拒绝,实现隔离功能,防止分裂-脑。2、为什么需要这一步来恢复进程中的日志?如果写入过程中写入失败,可能各个JN上的EditLog长度不同,需要恢复不一致的部分再开始写入。恢复机制如下:ANN首先向所有JN发送getJournalState请求;JN会返回一个Epoch(lastPromisedEpoch)给ANN;ANN收到JNs的大部分Epoch后,选择最好的加1作为当前新Epoch,然后向JN发送新的newEpoch请求,将新Epoch发送给JN;JN收到新的Epoch后,与lastPromisedEpoch进行比较,如果较大,则更新到本地,并返回给ANN自己的本地***EditLogSegmentstarttransactionId,如果较小,则返回NN错误;ANN在收到大部分JN响应后认为Epoch生成成功,开始准备日志恢复;ANN会选择一个***EditLogSegment事务ID作为恢复的依据,然后发送prepareRecovery给JN;RPCrequest,对应Paxos协议2p阶段的Phase1a,如果大部分JN成功响应prepareRecovery,则可以认为Phase1a成功;ANN选择一个数据源进行同步,向JN发送acceptRecoveryRPC请求,将数据源作为参数传递给JN。JN收到acceptRecovery请求后,会从JournalNodeHttpServer下载EditLogSegment,替换为本地保存的EditLogSegment,对应Paxos协议2p阶段的Phase1b。完成后会返回ANN请求成功状态。ANN在收到大部分JN的成功响应请求后,向JN发送finalizeLogSegment请求,表示数据恢复完成,以便之后所有JN上的日志保持一致。数据恢复后,ANN会将本地的in-processlog重命名为finalizedlog,形式为edits[start-txid][stop-txid]。3、ANN到JN的日志同步过程在上面日志同步步骤中有描述,如下:执行logSync过程,将ANN上的日志数据放入缓存队列中,并将缓存中的数据同步到JN,JN有相应的线程来处理logEdits请求。JN收到数据后,首先确认EpochNumber是否合法,然后验证日志事务ID是否正常,将日志刷盘,返回ANN成功码。抛出异常通过以上步骤,可以将日志成功同步到JN,并且可以保证JN日志的一致性,也可以保证在备NN上同步日志时的数据完整性和一致性。2.2.3QJM读取过程分析本读取过程针对备用神经网络(SNN)。SNN定时检查JournalNode上EditLog的变化,然后将EditLog拉回本地。SNN上有一个线程StandbyCheckpointer,它会周期性地合并SNN上的FSImage和EditLog,并将合并后的FSImage文件发送回主NN(ANN),也就是所谓的Checkpointing过程。让我们来看看检查点是如何工作的。在2.x版本中,原来由SecondaryNameNode主导的Checkpointing已经被SNN主导的Checkpointing取代。下面是Checkpoint的流程图:[图5.Checkpointing流程图]一般先在SNN上检查preconditions。前置条件包括两个方面:距上次Checkpointing的时间间隔和EditLog中事务的条目数限制。如果满足任何一个前提条件,就会触发Checkpointing,然后SNN会将最新的NameSpace数据,即SNN内存中当前状态的元数据,保存到一个临时的fsimage文件(fsimage.ckpt)中,并且将它与从JN中拉出的那个进行比较。***EditLog的事务ID,合并EditLog中所有fsimage.ckpt_中没有的元数据修改记录并重命名为一个新的fsimage文件,同时生成一个md5文件。通过HTTP请求将***fsimage传回ANN。定期合并fsimage有什么好处,主要有以下几个方面:可以防止EditLog越来越大,合并到新的fsimage后,可以删除旧的EditLog,避免主NN压力过大(ANN).merge在SNN上面可以保证fsimage保存了一个完整的元数据,避免故障恢复时数据丢失。3、要完成HA,除了元数据同步,还必须有完整的主备切换机制,Hadoop的主备选举依赖于ZooKeeper。下面是主备切换的状态图:[图6.Failover流程图]从图中可以看出,整个切换过程都是由ZKFC来控制的,可以分为三个组件:HealthMonitor、ZKFailoverController和ActiveStandbyElector。ZKFailoverController:是HealthMontior和ActiveStandbyElector的父体,进行具体的切换操作。HealthMonitor:监控NameNode的健康状态。如果状态异常,会触发回调ZKFailoverController,实现自动主备切换。ActiveStandbyElector:通知ZK进行主备选举。主备状态切换对应方法在故障转移过程中,ZooKeeper主要扮演什么角色?有以下几点:故障保护:集群中的每个NameNode都会在ZooKeeper中维护一个持久会话。当时间到了,故障迁移会触发ActiveNameNode的选择:ZooKeeper有一种选择ActiveNN的机制。一旦现有ANN宕机,其他NameNode可以向ZooKeeper申请独占访问成为下一个Active节点。防裂脑:ZK本身是强一致性和高可用的,可以用来保证同一时刻只有一个活动节点。什么场景下会触发自动切换?HDFS-2185总结了以下场景:ActiveNNJVM崩溃:ANN上的HealthMonitor状态报告会出现连接超时异常,HealthMonitor会触发状态转换为SERVICE_NOT_RESPONDING,然后ANN上的ZKFC退出选举,以及SNN上的ZKFC将获得ActiveLock,经过相应的隔离后成为Active节点。ActiveNNJVMfreezes:这意味着JVM没有崩溃,但是无法响应。就像撞车一样,会触发自动切换。ActiveNN机器宕机:此时ActiveStandbyElector会与ZK失去心跳,会话超时。SNN上的ZKFC会通知ZK删除ANN的主动锁,并在相应隔离后完成主备切换。ActiveNN健康状态异常:此时HealthMonitor会收到HealthCheckFailedException,触发自动切换。ActiveZKFCcrash:虽然ZKFC是一个独立的进程,但是由于设计简单,很容易出现问题。一旦ZKFC进程挂了,虽然此时NameNode是OK的,但是系统也认为需要切换。此时SNN会发送一个请求当ANN请求ANN让出主节点的位置,ANN收到请求后会触发自动切换完成。ZooKeeper崩溃:如果ZK崩溃,主备NN上的ZKFC将感知断开连接。此时,主备神经网络会进入NeutralMode模式,在不改变主备神经网络状态的情况下继续工作。但是此时,如果ANN也出现故障,则集群无法进行Failover,不可用。所以对于这种场景,ZK一般是不允许挂到多台机器上的,至少需要N/2+1台机器维护服务才算安全。.五、小结上面介绍了HadoopHA机制,归纳起来主要有两部分:元数据同步和主备选举。元数据同步依赖QJM共享存储,主备选举依赖ZKFC和Zookeeper。整个过程还是比较复杂的。如果你能理解Paxos协议,你就能更好地理解它。我希望这篇文章能让您更好地了解HA。