下图为master的日志记录:
从上图可以看到zk中/Hbase/replication/rs
的节点信息无法获取,然后我们立马去检查zk日志发现zk已经全部断联而且已经瘫痪,截图如下:
上网一番查找发现java.nio.channels.CancelledKeyException
是zk 3.4.10
之前版本的bug,笔者使用的正是3.4.8
,此bug在3.4.10已修复。
至此,zk宕机原因找到了,将zk节点全部重启然后重启HBase,核心问题来了,遇到了一个老兄弟,java.io.IOException: Packet len6075380 is out of range !
master退出,启动失败。
按照以前的经验,去zkCli.sh修改了-Djute.maxbuffer=41943040
的配置,第一次修改直接将值翻倍后重启还是报同样的错误,多次翻倍终无效果;一气之下直接将值多加了两个00,变成了之前的上千倍,数值达到了百亿级别,重启依然无效。然后决定删除zk中的HBase节点,rmr /habse竟然报错了,更巧的是同样的java.io.IOException: Packet len6075380 is out of range !
好难受啊!!!
折腾了这么半天没有思路了,突然运维的同事提醒我说我改的值应该已经超出zk的限制了,估计都不会生效,听了这么一个提醒,心里窃喜的很啊,马上一番操作下载zk源码看看到底怎么回事。
代码语言:javascript复制void readLength() throws IOException {
int len = incomingBuffer.getInt();
if (len < 0 || len >= packetLen) {
throw new IOException("Packet len" len " is out of range!");
}
incomingBuffer = ByteBuffer.allocate(len);
}
packetLen为配置文件的值,而incomingBuffer为HBase启动报错的值:6075380 > 4194304(默认值),所以抛出上述异常:
代码语言:javascript复制private int packetLen = ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT;
查看源码发现zk初始化的时候会去加载配置文件的·jute.maxbuffer·值,如果值的大小不为空就采用此值,如果为空就用默认值,上图红色部分为默认是大小4M=4194304,但还有一个前提就是配置的值必须是int范围内的值,否则Integer.parseInt(value.trim())方法会报错。
代码语言:javascript复制 try {
packetLen = clientConfig.getInt(ZKConfig.JUTE_MAXBUFFER,
ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT);
LOG.info("{} value is {} Bytes", ZKConfig.JUTE_MAXBUFFER,
packetLen);
} catch (NumberFormatException e) {
String msg = MessageFormat.format(
"Configured value {0} for property {1} can not be parsed to int",
clientConfig.getProperty(ZKConfig.JUTE_MAXBUFFER),
ZKConfig.JUTE_MAXBUFFER);
LOG.error(msg);
throw new IOException(msg);
}
}
public int getInt(String key, int defaultValue) {
String value = getProperty(key);
if (value != null) {
return Integer.parseInt(value.trim());
}
return defaultValue;
}
而笔者最后修改的值为百亿,已然超出了int范围,所以是无效的配置。至此,原因找到了,立马改成了10M的配置重新启动zk并删除/hbase节点成功。
但是,调整此参数始终治标不治本,结合本司集群中的数据量(200亿 )和region数量(10W )考虑,master重启是一项浩大的工程,因为重启会导致region的状态变更,而变更的中间状态是在zk节点维护,当大量的region需要分配,而分配速度又很慢就会导致zk中/hbase/下的部分节点存储数据量太大从而导致异常。
所以我们需要从两个方面入手:
1、尽量合理的划分region。
2、提高处理region分配的线程数。修改配置项hbase.assignment.threads.max
,该配置默认值为30。
<property>
<name>hbase.assignment.threads.max</name>
<value>100</value>
</property>
然后重启HBase,顺利启动。