@@ -56,6 +56,41 @@ NameNode 实现主备切换的流程下图所示:
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				5.  ActiveStandbyElector 在主备选举完成后,会回调 ZKFailoverController 的相应方法来通知当前的 NameNode 成为主 NameNode 或备 NameNode。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				6.  ZKFailoverController 调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法将 NameNode 转换为 Active 状态或 Standby 状态。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				#### 1.3.1 自动触发主备选举 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				 NameNode 在选举成功后,会在 zk 上创建了一个 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点,而没有选举成功的备 NameNode 会监控这个节点,通过 Watcher 来监听这个节点的状态变化事件,  ZKFC 的 ActiveStandbyElector 主要关注这个节点的 NodeDeleted 事件(这部分实现跟 Kafka 中 Controller 的选举一样)。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				如果 Active NameNode 对应的 HealthMonitor 检测到 NameNode 的状态异常时, ZKFailoverController 会主动删除当前在 Zookeeper 上建立的临时节点 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock,  这样处于 Standby 状态的 NameNode 的 ActiveStandbyElector 注册的监听器就会收到这个节点的 NodeDeleted 事件。收到这个事件之后,会马上再次进入到创建 /hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点的流程,如果创建成功,这个本来处于 Standby 状态的 NameNode 就选举为主 NameNode 并随后开始切换为 Active 状态。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				当然,如果是 Active 状态的 NameNode 所在的机器整个宕掉的话,那么根据 Zookeeper 的临时节点特性,/hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				#### 1.3.2 HDFS 脑裂问题 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				 在实际中,  NameNode 可能会出现这种情况,  NameNode 在垃圾回收(  GC)  时,  可能会在长时间内整个系统无响应,  因此,  也就无法向 zk 写入心跳信息,这样的话可能会导致临时节点掉线,备 NameNode 会切换到 Active 状态,这种情况,可能会导致整个集群会有同时有两个 NameNode,  这就是脑裂问题。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				脑裂问题的解决方案是隔离(  Fencing) ,  主要是在以下三处采用隔离措施: 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				第三方共享存储:任一时刻,只有一个 NN 可以写入;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				DataNode:  需要保证只有一个 NN 发出与管理数据副本有关的删除命令;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				Client:  需要保证同一时刻只有一个 NN 能够对 Client 的请求发出正确的响应。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				关于这个问题目前解决方案的实现如下:
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				ActiveStandbyElector 为了实现 fencing,  会在成功创建 Zookeeper 节点 hadoop-ha/${dfs.nameservices}/ActiveStandbyElectorLock 从而成为 Active NameNode 之后,创建另外一个路径为 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 的持久节点,这个节点里面保存了这个 Active NameNode 的地址信息;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				Active NameNode 的 ActiveStandbyElector 在正常的状态下关闭 Zookeeper Session 的时候,会一起删除这个持久节点;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				但如果 ActiveStandbyElector 在异常的状态下 Zookeeper Session 关闭 (比如前述的 Zookeeper 假死),那么由于 /hadoop-ha/${dfs.nameservices}/ActiveBreadCrumb 是持久节点,会一直保留下来,后面当另一个 NameNode 选主成功之后,会注意到上一个 Active NameNode 遗留下来的这个节点,从而会回调 ZKFailoverController 的方法对旧的 Active NameNode 进行 fencing。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				在进行 fencing 的时候,会执行以下的操作:
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				Hadoop 目前主要提供两种隔离措施,通常会选择第一种:
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				sshfence:  通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				shellfence:  执行一个用户自定义的 shell 脚本来将对应的进程隔离。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				只有在成功地执行完成 fencing 之后,选主成功的 ActiveStandbyElector 才会回调 ZKFailoverController 的 becomeActive 方法将对应的 NameNode 转换为 Active 状态,开始对外提供服务。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				NameNode 选举的实现机制与 Kafka 的 Controller 类似,那么 Kafka 是如何避免脑裂问题的呢?
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				Controller 给 Broker 发送的请求中,都会携带 controller epoch 信息,如果 broker 发现当前请求的 epoch 小于缓存中的值,那么就证明这是来自旧 Controller 的请求,就会决绝这个请求,正常情况下是没什么问题的;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				但是异常情况下呢?如果 Broker 先收到异常 Controller 的请求进行处理呢?现在看 Kafka 在这一部分并没有适合的方案;
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				正常情况下,  Kafka 新的 Controller 选举出来之后,  Controller 会向全局所有 broker 发送一个 metadata 请求,这样全局所有 Broker 都可以知道当前最新的 controller epoch,  但是并不能保证可以完全避免上面这个问题,  还是有出现这个问题的几率的,  只不过非常小,  而且即使出现了由于 Kafka 的高可靠架构,影响也非常有限,至少从目前看,这个问题并不是严重的问题。
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				   
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				### 1.4 YARN高可用 
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
	
		
			
				
					
					
						
					 
				
			
			 
			 
			
				@@ -513,5 +548,5 @@ yarn-daemon.sh start resourcemanager
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				[Hadoop NameNode 高可用 (High Availability) 实现解析 ](https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-name-node/index.html )
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				[hdfs架构详解(防脑裂fencing机制值得学习) ](https://www.cnblogs.com/lushilin/p/11239908.html )
 
			
		 
		
	
		
			
				 
				 
			
			 
			 
			
				< div  align = "center" >  < img   src = "https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/weixin-desc.png" />  </ div >