【概述】
最近连续在多个环境中遇到了同一个问题:在HA模式下,两个resourcemanager均为standby,并且持续没有选举出新的leader。经过一番分析,并对照源码梳理问题出现前后的逻辑流程,最后发现是因为zk会话过期(session expire)引起的问题,本文就复盘总结下。
【RM的正常选举流程】
在很早之前的文章中,介绍过hadoop里namenode的HA机制(戳这里),RM的选举流程其实是复用了同样的框架,只是以一个独立线程的方式运行,而不是像namenode一样,有个独立的进程(zkfc)负责与zk连接并选举。
因此,整体的选举流程会和namenode的选举方式基本雷同,即首先向zk建立连接,当连接建立成功后,在zk上竞争创建临时锁节点,成功创建的rm成为active,失败的则成为standby。
【与zk之间网络异常后的情况】
正常逻辑是相对简单的,那我们再来看看与zk之间网络出现异常,以及网络异常恢复之后的处理逻辑,具体如下图所示:
1. 当ZK服务出现故障,或者网络出现故障,导致网络完全不可达时,客户端与ZK的连接会出现在指定时间内没有读到任何数据,从而引发会话超时。(也可能是读异常,此时产生的是EndOfStreamException,后续处理逻辑与会话超时的逻辑一样)。
这个时候,zk客户端的发送线程会抛会话超时的异常,同时内部捕获该异常, 向事件回调线程的队列中插入连接断开的事件。此后,循环执行与zk的重连动作。
while (state.isAlive()) {
try {
...
if (to <= 0) {
String warnInfo;
warnInfo =
"Client session timed out, have not heard from server in " +
clientCnxnSocket.getIdleRecv() + "ms" +
" for sessionid 0x" + Long.toHexString(sessionId);
LOG.warn(warnInfo);
throw new SessionTimeoutException(warnInfo);
}
} catch (Throwable e) {
...
if (state.isAlive()) {
eventThread.queueEvent(
new WatchedEvent(Event.EventType.None, Event.KeeperState.Disconnected, null));
}
...
}
}
2. zk客户端中的事件回调线程接收到事件后,向上进行回调通知。在RM的回调处理中,启动定时器线程,触发成为standby。
synchronized void processWatchEvent(ZooKeeper zk, WatchedEvent event) {
...
if (eventType == Event.EventType.None) {
switch (event.getState()) {
case Disconnected:
LOG.info("Session disconnected. Entering neutral mode...");
zkConnectionState = ConnectionState.DISCONNECTED;
enterNeutralMode();
break;
...
}
}
}
private vo