记录一次由OOM引发zookeeper集群宕机的生产故障

一. 问题回顾

1.1 zookeeper实例宕机

1.发布重启应用的时候,应用有大量报错:

在这里插入图片描述

2.尚未开始发布的共用同一套zookeeper集群的另外一套应用集群,也有大量报错:

在这里插入图片描述
3.观察生产的监控,发现生产3个zookeeper实例,当时有2个出现了宕机。且监控已经无法获取其数据。

在这里插入图片描述

1.2 异常日志

查看已宕机的zookeeper实例的服务端日志,发现有报OOM异常

在这里插入图片描述

二. 问题复现

2.1 集群搭建

自己搭建一套zk集群,1leader,2follower
server.1=10.172.33.37:2888:3888
server.2=10.172.33.38:2888:3888
server.3=10.172.33.39:2888:3888

2.2 数据写入

模拟生产zookeeper存储的数据情况,在上述集群,/gateway/apiInfo路径下写入约2000条数据

2.3 配置jvm参数

1.添加配置文件:

在这里插入图片描述
2.添加配置:
export JAVA_HOME=/u02/java/jdk1.8.0_131
export JVMFLAGS=“-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/u02/phoenix/zookeeper/zookeeper-3.4.14/conf $JVMFLAGS”

在这里插入图片描述
参数含义:
-XX:+HeapDumpOnOutOfMemoryError参数表示当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=目录 参数表示生成DUMP文件的路径,也可以指定文件名称,例如:-XX:HeapDumpPath=目录/java_heapdump.hprof。

2.4 客户端启动

本地应用,连接上述搭建的zookeeper,同时启动10个实例。在启动完后,没多久发现有2个zookeeper实例(10.172.33.38、10.172.33.39)挂了,查看zookeeper服务端日志,发现有报错: java.lang.OutOfMemoryError:GC overhead limit exceeded
同时在目录 /u02/phoenix/zookeeper/zookeeper-3.4.14/conf 下观察到有DUMP文件(java_pid10231.hprof)生成

在这里插入图片描述

三. 原因分析

3.1 DUMP文件快照内容分析

1.VisualVM工具导入DUMP文件。点击选项切换到Objects

在这里插入图片描述
2.从分析结果来看,char、String、HashMap$Node这3个对象无论是实例数还是大小,占据的比例都是比较大的。

在这里插入图片描述

3.进一步分析char、String、HashMap$Node的实例,发现基本都和/gateway/apiInfo路径下的数据有关。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
4.分析多个char实例的引用,发现其逐层被String、HashMap$Node对象下的实例所引用。最终基本都指向了org.apache.zookeeper.server.WatchManager对象下的watchTable以及watch2Paths对象。

在这里插入图片描述

3.2 源码分析

1.分析zookeeper服务端源码,发现org.apache.zookeeper.server.WatchManager类下有watchTable以及watch2Path两个成员变量。

在这里插入图片描述
说明:
WatcherManager:WatcherManager是ZooKeeper服务端Watcher的管理者,负责watcher的存储、触发以及移除那些已经被触发的Watcher。服务端接收 Watcher 并存储接收到客户端请求,处理请求判断是否需要注册 Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn 代表一个客户端和服务端的连接,实现了 Watcher 的 process 接口,此时可以看成一个 Watcher 对象)存储在WatcherManager 的 WatchTable 和 watch2Paths 中去。watchTable表示从节点路径到watcher集合的映射,而watch2Paths则表示从watcher到所有节点路径集合的映射。

Watcher的用途:
一、用于监听节点数据产生的变化,在zk中可以配置集群的通用配置,当配置数据发生了变化之后通知所有订阅该节点的Watcher,该节点发生事件类型
二、用于监听节点状态的变化,比如创建一个节点、删除一个节点等对节点的操作
三、管理客户端与服务端连接的生命周期

2.WatchManager类内部,包含addWatch方法,该方法代码比较简单,其实就是建立节点路径到watcher集合的映射,以及watcher到节点路径集合的映射。

在这里插入图片描述

3.3 原因总结

1.结合当时上线时,zookeeper客户端应用,新增加了对前缀为/apiNameList/的path的监听,前缀为/apiNameList/的path总数据量大约有1万,生产应用大约有100个实例,即总的Watcher将达到100,再结合上述源码分析,可得知在启动的时候,会调用WatcherManager内部的addWatch方法,将会占用WatcherManager 的 WatchTable 和 watch2Paths大量内存。这和VisualVM分析过的堆内存占用情况,是吻合的。

2.总的新增的watches的数量(Watch Count),将增加1万(path数量)* 100 (Watcher数量)≈ 100万,这和在监控图上,看到的现象是一致的。

在这里插入图片描述

四. 优化方案

4.1 优化方案

1.修改zookeeper服务端jvm参数,通过-Xms -Xmx增加堆大小。对zookeeper所在机器进行扩容,由原来的4G内存,扩容至8G。
2.新增监听路径,用于某块业务功能。去除原来的监听(原来的监听,对于应用来说有大量的无用数据,应用监听会对zookeeper带来性能负担)

the end~
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值