基本概念

zkfc是什么?ZooKeeperFailoverController
它是什么?是Hadoop中通过ZK实现FC功能的一个实用工具。
主要作用:作为一个ZK集群的客户端,用来监控NN的状态信息,主备NN的切换
谁会用它?每个运行NN的节点必须要运行一个zkfc





有啥功能?

1.Health monitoring (健康监测) 对NN的健康监测

zkfc定期对本地的NN发起health-check的命令,如果NN正确返回,那么这个NN被认为是OK的。否则被认为是失效节点。

2.ZooKeeper Session Management ( 作为一个ZK集群的客户端, 管理与zk的会话 )

当本地NN是健康的时候,zkfc将会在zk中持有一个session。如果本地NN又正好是active的,那么zkfc还有持有一个”ephemeral”的节点作为锁,一旦本地NN失效了,那么这个节点将会被自动删除。

3.ZooKeeper-based election ( 主备NN选举, 主备NN的切换)

如果本地NN是健康的,并且zkfc发现没有其他的NN持有那个独占锁。那么他将试图去获取该锁,一旦成功,那么它就需要执行故障转移(Failover),然后成为active的NN节点。Failover的过程是:第一步,对之前的NN执行fence,如果需要的话。第二步,将本地NN转换到active状态。
另外:
如果一个Active因HealthMonitor监控到状态异常,这里会作出判断,先通过隔离(Fencing)功能关闭它(确保关闭或者不能提供服务),然后在ZK上删除它对应ZNode。
发生上述事件后,在另外一台机器上的ZKFC中的ActiveStandbyElector 会收到事件,并重新进行选举(尝试创建特定ZNode),它将获得成功并更改NN中状态,从而实现Active节点的变更






(一) 基本原理

zk(zookeeper)的基本特性:

(1) 可靠存储小量数据且提供强一致性 
(2) ephemeral node(创建的锁节点), 在创建它的客户端关闭后,可以自动删除 
(3) 对于node状态的变化,可以提供异步的通知(watcher)

zk在zkfc中可以提供的功能:

(1) Failure detector(通过watcher监听机制实现): 及时发现出故障的NN,并通知zkfc 
(2) Active node locator: 帮助客户端定位哪个是Active的NN 
(3) Mutual exclusion of active state(通过加锁): 保证某一时刻只有一个Active的NN

(二) 模块

(1) ZKFailoverController(DFSZKFailoverController): 驱动整个ZKFC的运转,通过向HealthMonitor和ActiveStandbyElector注册回调函数的方式,subscribe HealthMonitor和ActiveStandbyElector的事件,并做相应的处理

(2) HealthMonitor: 定期check NN的健康状况,在NN健康状况发生变化时,通过回调函数把变化通知给ZKFailoverController

(3) ActiveStandbyElector: 管理NN在zookeeper上的状态,调用zookeeper客户端API监控zookeeper上结点的状态,zookeeper上对应node的结点发生变化时,通过回调函数把变化通知给ZKFailoverController

(4) FailoverController: 提供做graceful failover的相关功能(dfs admin可以通过命令行工具手工发起failover)
在这里插入图片描述

(三) 线程模型

ZKFC的线程模型主要包括三类线程,
一是 主线程
二是 HealthMonitor线程
三是 zookeeper客户端的线

它们的主要工作方式是

(1)主线程在启动所有的服务后就开始循环等待 

(2) HealthMonitor是一个单独的线程,它定期向NN发包,检查NN的健康状况 
当NN的状态发生变化时,HealthMonitor线程会回调ZKFailoverController注册进来的回调函数,通知ZKFailoverController NN的状态发生了变化 
ZKFailoverController收到通知后,会调用ActiveStandbyElector的API,来管理在zookeeper上的结点的状态 

(3) ActiveStandbyElector会调用zookeeper客户端API监控zookeeper上结点的状态,发生变化时,回调ZKFailoverController的回调函数,
通知ZKFailoverController,做出相应的变化

(四) 自动触发主备选举

  1. NameNode 在选举成功后,ActiveStandbyElector会在 zk 上创建了一个/hadoopha/${dfs.nameservices}/ActiveStandbyElectorLock 临时节点,而没有选举成功的备 NameNode 中的 ActiveStandbyElector会监控这个节点,通过 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连接的客户端线程也挂了,会话结束,那么根据 Zookeepe的临时节点特性,/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换

(五) HDFS 脑裂问题

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

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

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

解决方案的实现如下:(在NN上下手)

  1. ActiveStandbyElector 为了实现 fencing,会在成功创建 Zookeeper 临时节点 hadoop-ha/ d f s . n a m e s e r v i c e s / A c t i v e S t a n d b y E l e c t o r L o c k 成 为 A c t i v e N a m e N o d e 之 后 , 创 建 另 外 一 个 路 径 为 h a d o o p − h a / {dfs.nameservices}/ActiveStandbyElectorLock 成为 Active NameNode 之后,创建另外一个路径为hadoop-ha/ dfs.nameservices/ActiveStandbyElectorLockActiveNameNodehadoopha/{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 目前主要提供两种隔离措施,通常会选择第一种:
     
    1)	sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;
    2)	shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离。
    

只有在成功地执行完成 fencing 之后,选主成功的 ActiveStandbyElector 才会回调 ZKFailoverController 的 becomeActive 方法将对应的 NameNode 转换为 Active 状态,开始对外提供服务。




Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐