0%

Hadoop常见知识点

1. HDFS架构

4093A846-0B4E-4EF1-8DF2-5F175350D92D_4_5005_c.jpeg

  1. Active NameNode和 Standby NameNode:两台NameNode形成互备,处于Active状态的为主NameNode,另一台处于StandBy状态,为备用NameNode,主要主NameNode对外提供读写服务。
  2. ZKFailoverController(主备切换控制器,FC):ZKFailoverController 作为独立的进程运行,对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况,在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换(当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换);
  3. Zookeeper 集群:为主备切换控制器提供主备选举支持;
  4. 共享存储系统:共享存储系统是实现 NameNode 的高可用最为关键的部分,共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和备 NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。
  5. DataNode 节点:因为主 NameNode 和备 NameNode 需要共享 HDFS 的数据块和 DataNode 之间的映射关系,为了使故障切换能够快速进行,DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。

FailoverController
FC 最初的目的是为了实现 SNN 和 ANN 之间故障自动切换,FC 是独立与 NN 之外的故障切换控制器,ZKFC 作为 NameNode 机器上一个独立的进程启动 ,它启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两个主要的内部组件,其中:

  1. HealthMonitor:主要负责检测 NameNode 的健康状态,如果检测到 NameNode 的状态发生变化,会回调 ZKFailoverController 的相应方法进行自动的主备选举;
  2. ActiveStandbyElector:主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑,一旦 Zookeeper 主备选举完成,会回调 ZKFailoverController 的相应方法来进行 NameNode 的主备状态切换。

自动触发主备选举

  1. NameNode 在选举成功后,会在 zk 上创建了一个 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点,而没有选举成功的备 NameNode 会监控这个节点,通过 Watcher 来监听这个节点的状态变化事件,ZKFC 的 ActiveStandbyElector 主要关注这个节点的 NodeDeleted 事件(这部分实现跟 Kafka 中 Controller 的选举一样)。

  2. 如果 Active NameNode 对应的 HealthMonitor 检测到 NameNode 的状态异常时, ZKFailoverController 会主动删除当前在 Zookeeper 上建立的临时节点 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock,这样处于 Standby 状态的 NameNode 的 ActiveStandbyElector 注册的监听器就会收到这个节点的 NodeDeleted 事件。收到这个事件之后,会马上再次进入到创建 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点的流程,如果创建成功,这个本来处于 Standby 状态的 NameNode 就选举为主 NameNode 并随后开始切换为 Active 状态。

  3. 当然,如果是 Active 状态的 NameNode 所在的机器整个宕掉的话,那么根据 Zookeeper 的临时节点特性,/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换。

HDFS 脑裂问题

在实际中,NameNode 可能会出现这种情况,NameNode 在垃圾回收(GC)时,可能会在长时间内整个系统无响应,因此,也就无法向 zk 写入心跳信息,这样的话可能会导致临时节点掉线,备 NameNode 会切换到 Active 状态,这种情况,可能会导致整个集群会有同时有两个 NameNode,这就是脑裂问题。

脑裂问题的解决方案是隔离(Fencing),主要是在以下三处采用隔离措施:

  • 第三方共享存储:任一时刻,只有一个 NN 可以写入;
  • DataNode:需要保证只有一个 NN 发出与管理数据副本有关的删除命令;
  • Client:需要保证同一时刻只有一个 NN 能够对 Client 的请求发出正确的响应。

关于这个问题目前解决方案的实现如下:

  1. ActiveStandbyElector 为了实现 fencing,会在成功创建 Zookeeper 节点 hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 从而成为 Active NameNode 之后,创建另外一个路径为 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 的持久节点,这个节点里面保存了这个 Active NameNode 的地址信息
  2. Active NameNode 的 ActiveStandbyElector 在正常的状态下关闭 Zookeeper Session 的时候,会一起删除这个持久节点;
  3. 但如果 ActiveStandbyElector 在异常的状态下 Zookeeper Session 关闭 (比如前述的 Zookeeper 假死),那么由于 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 是持久节点,会一直保留下来,后面当另一个 NameNode 选主成功之后,会注意到上一个 Active NameNode 遗留下来的这个节点,从而会回调 ZKFailoverController 的方法对旧的 Active NameNode 进行 fencing。
    在进行 fencing 的时候,会执行以下的操作:

    1. 首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态;
    2. 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施。

Hadoop 目前主要提供两种隔离措施,通常会选择第一种:

  • sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;
  • shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离。
    只有在成功地执行完成 fencing 之后,选主成功的 ActiveStandbyElector 才会回调 ZKFailoverController 的 becomeActive 方法将对应的 NameNode 转换为 Active 状态,开始对外提供服务。

HDFS 2.0 Federation 实现

在 1.0 中,HDFS 的架构设计有以下缺点:

  1. namespace 扩展性差:在单一的 NN 情况下,因为所有 namespace 数据都需要加载到内存,所以物理机内存的大小限制了整个 HDFS 能够容纳文件的最大个数(namespace 指的是 HDFS 中树形目录和文件结构以及文件对应的 block 信息);
  2. 性能可扩展性差:由于所有请求都需要经过 NN,单一 NN 导致所有请求都由一台机器进行处理,很容易达到单台机器的吞吐;
  3. 隔离性差:多租户的情况下,单一 NN 的架构无法在租户间进行隔离,会造成不可避免的相互影响。
Federation 架构

Federation 的架构设计如下图所示(图片来自 HDFS Federation):
8786F522-742E-4650-913D-392BDAB938DA_4_5005_c.jpeg

Federation 的核心设计思想

Federation 的核心思想是将一个大的 namespace 划分多个子 namespace,并且每个 namespace 分别由单独的 NameNode 负责,这些 NameNode 之间互相独立,不会影响,不需要做任何协调工作(其实跟拆集群有一些相似),集群的所有 DataNode 会被多个 NameNode 共享。
其中,每个子 namespace 和 DataNode 之间会由数据块管理层作为中介建立映射关系,数据块管理层由若干数据块池(Pool)构成,每个数据块只会唯一属于某个固定的数据块池,而一个子 namespace 可以对应多个数据块池。每个 DataNode 需要向集群中所有的 NameNode 注册,且周期性地向所有 NameNode 发送心跳和块报告,并执行来自所有 NameNode 的命令。
一个 block pool 由属于同一个 namespace 的数据块组成,每个 DataNode 可能会存储集群中所有 block pool 的数据块;
每个 block pool 内部自治,也就是说各自管理各自的 block,不会与其他 block pool 交流,如果一个 NameNode 挂掉了,不会影响其他 NameNode;
某个 NameNode 上的 namespace 和它对应的 block pool 一起被称为 namespace volume,它是管理的基本单位。当一个 NameNode/namespace 被删除后,其所有 DataNode 上对应的 block pool 也会被删除,当集群升级时,每个 namespace volume 可以作为一个基本单元进行升级。

2. Yarn架构

待完成

3. MapReduce过程

4. Yarn 调度MapReduce

5. HDFS写流程

  1. Client调用DistributedFileSystem对象的create方法,创建一个文件输出流(FSDataOutputStream)对象。
  2. 通过DistributedFileSystem对象与集群的NameNode进行一次RPC远程调用,在HDFS的Namespace中创建一个文件条目(Entry),此时该条目没有任何block,NameNode会返回该数据每个块需要拷贝的DataNode地址信息。
  3. 通过FSDataOutputStream对象,开始向DataNode写入数据,数据首先被写入FSDataOutputStream对象内部的数据队列中,数据队列由DataStreamer使用,它通过选择合适的DataNode列表来存储副本,从而要求NameNode分配新的block;
  4. DataStreamer将数据包以流式传输的方式传输到分配的第一个DataNode中,该数据流将数据包存储到第一个 DataNode 中并将其转发到第二个 DataNode 中,接着第二个 DataNode 节点会将数据包转发到第三个 DataNode 节点;
  5. DataNode 确认数据传输完成,最后由第一个 DataNode 通知 client 数据写入成功;
  6. 完成向文件写入数据,Client 在文件输出流(FSDataOutputStream)对象上调用 close 方法,完成文件写入;
  7. 调用 DistributedFileSystem 对象的 complete 方法,通知 NameNode 文件写入成功,NameNode 会将相关结果记录到 editlog 中。

6. HDFS读流程

  1. Client 通过 DistributedFileSystem 对象与集群的 NameNode 进行一次 RPC 远程调用,获取文件 block 位置信息;
  2. NameNode 返回存储的每个块的 DataNode 列表;
  3. Client 将连接到列表中最近的 DataNode;
  4. Client 开始从 DataNode 并行读取数据;
  5. 一旦 Client 获得了所有必须的 block,它就会将这些 block 组合起来形成一个文件。

7. hdfs创建一个文件的流程

  1. 客户端通过ClientProtocol协议向RpcServer发起创建文件的RPC请求。
  2. FSNamesystem封装了各种HDFS操作的实现细节,RpcServer调用FSNamesystem中的相关方法以创建目录。
  3. 进一步的,FSDirectory封装了各种目录树操作的实现细节,FSNamesystem调用FSDirectory中的相关方法在目录树中创建目标文件,并通过日志系统备份文件系统的修改。
  4. 最后,RpcServer将RPC响应返回给客户端。

8. hadoop1.x 和hadoop 2.x 的区别

  1. 资源调度方式的改变
    在1.x, 使用Jobtracker负责任务调度和资源管理,单点负担过重,在2.x中,新增了yarn作为集群的调度工具.在yarn中,使用ResourceManager进行 资源管理, 单独开启一个Container作为ApplicationMaster来进行任务管理.

  2. HA模式
    在1.x中没有HA模式,集群中只有一个NameNode,而在2.x中可以启用HA模式,存在一个Active NameNode 和Standby NameNode.

  3. HDFS Federation
    Hadoop 2.0中对HDFS进行了改进,使NameNode可以横向扩展成多个,每个NameNode分管一部分目录,进而产生了HDFS Federation,该机制的引入不仅增强了HDFS的扩展性,也使HDFS具备了隔离性

9. hadoop HA介绍

  1. Active NameNode 和 Standby NameNode:两台 NameNode 形成互备,一台处于 Active 状态,为主 NameNode,另外一台处于 Standby 状态,为备 NameNode,只有主 NameNode 才能对外提供读写服务;
  2. ZKFailoverController(主备切换控制器,FC):ZKFailoverController 作为独立的进程运行,对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况,在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换(当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换);
  3. Zookeeper 集群:为主备切换控制器提供主备选举支持;
    共享存储系统:共享存储系统是实现 NameNode 的高可用最为关键的部分,共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。主 NameNode 和备 NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。
  4. DataNode 节点:因为主 NameNode 和备 NameNode 需要共享 HDFS 的数据块和 DataNode 之间的映射关系,为了使故障切换能够快速进行,DataNode 会同时向主 NameNode 和备 NameNode 上报数据块的位置信息。

10. 小文件过多会有什么危害,如何避免?

Hadoop上大量HDFS元数据信息存储在NameNode内存中,因此过多的小文件必定会压垮NameNode的内存.

每个元数据对象约占150byte,所以如果有1千万个小文件,每个文件占用一个block,则NameNode大约需要2G空间。如果存储1亿个文件,则NameNode需要20G空间.

显而易见的解决这个问题的方法就是合并小文件,可以选择在客户端上传时执行一定的策略先合并,或者是使用Hadoop的CombineFileInputFormat<K,V>实现小文件的合并

11. 启动hadoop集群会分别启动哪些进程,各自的作用

  • NameNode:
    维护文件系统树及整棵树内所有的文件和目录。这些信息永久保存在本地磁盘的两个文件中:命名空间镜像文件、编辑日志文件
    记录每个文件中各个块所在的数据节点信息,这些信息在内存中保存,每次启动系统时重建这些信息
    负责响应客户端的 数据块位置请求 。也就是客户端想存数据,应该往哪些节点的哪些块存;客户端想取数据,应该到哪些节点取
    接受记录在数据存取过程中,datanode节点报告过来的故障、损坏信息

  • SecondaryNameNode(非HA模式):
    实现namenode容错的一种机制。定期合并编辑日志与命名空间镜像,当namenode挂掉时,可通过一定步骤进行上顶。(注意 并不是NameNode的备用节点)

  • DataNode:
    根据需要存取并检索数据块
    定期向namenode发送其存储的数据块列表

  • ResourceManager:
    负责Job的调度,将一个任务与一个NodeManager相匹配。也就是将一个MapReduce之类的任务分配给一个从节点的NodeManager来执行。

  • NodeManager:
    运行ResourceManager分配的任务,同时将任务进度向application master报告

  • JournalNode(HA下启用):
    高可用情况下存放namenode的editlog文件