zookeeper OOM问题排查

ZooKeeper内存泄漏分析
本文分析了一次在ZooKeeper 3.3.3版本中遇到的内存泄漏问题,详细探讨了问题的原因在于Leader类的observingLearners成员变量未能正确释放Observer句柄,最终导致内存占用持续增长。

背景

最近折腾的数据库同步项目中,大量使用了zookeeper(版本3.3.3),可以说是强依赖,但是最近频频出现zookeeper内存使用率达到100%,而且是GC不掉,直接导致整个系统挂起,伤不起阿

 

分析

因为大部分的情况都是无法GC回收,所以很大程度上怀疑出现memory leak。

设置了jvm参数,收集了一下OOM导致jvm crash之后的日志文件进行分析

 

-XX:+HeapDumpOnOutOfMemoryError

leak分析: 



 

从leak分析来看,比较明显,99%的内存都被Leader类的observingLearners给吃光了,所以重点就可以落在zookeeper observer的使用上了。

zookeeper的observer模式

官方说明: http://zookeeper.apache.org/doc/trunk/zookeeperObservers.html 

主要用于解决读扩展性的问题,observer的节点不参与vote,也就是说写操作都只会发生在leader/follower中进行投票决策,而observer就是一个只读镜像。

但有一点和数据库的master/slave模式不同的是,observer也会接受写请求,但会将请求转交给leader/follower集群进行处理,并同步等待返回结果。

 

可以说observer比较巧妙的解决了读扩展性的问题,在zookeeper3.4.5版本,增加了readonlymode,和observer模式还是有所不同。

 

在我之前的文章中,zookeeper项目使用几点小结,有描述在项目中使用observer的情况: 


从图中可以看出: 

1.  整个zookeeper大集群有2部分组成,杭州的一个leader/follower集群  +  美国的一个observer集群

2.  为保证可用性,杭州集群的机器分别部署在3个机房中,(满足任意机房AB, 机房A+机房B  >  机房A+机房B+机房C/2),最小的部署结构为3+2+2机器,这样可以确保,任何一个机房挂了,都可以保证整个zookeeper集群的可用性

 

代码分析:

有了以上的背景分析,再回到memory leak问题上来,翻了下zookeeper issue,发现还真有提交对应的memory leak问题,https://issues.apache.org/jira/browse/ZOOKEEPER-1303 

看完issue后,这时候问题已经明显了。 

 

在Leader.java类中:

 

/**
     * Remove the learner from the learner list
     * 
     * @param peer
     */
    void removeLearnerHandler(LearnerHandler peer) {
        synchronized (forwardingFollowers) {
            forwardingFollowers.remove(peer);            
        }        
        synchronized (learners) {
            learners.remove(peer);
        }
    }

 

 

这里面Leader节点,在与对应的follower/observer之间的链接异常断开时,会清理当前内存中的引用句柄 (不然下次的vote信息还会发送到挂了的节点上)。

而leader在往observer上推送write数据,会遍历当前内存中的observingLearners列表

 

/**
     * send a packet to all observers     
     */
    void sendObserverPacket(QuorumPacket qp) {        
        synchronized(observingLearners) {
            for (LearnerHandler f : observingLearners) {                
                f.queuePacket(qp);
            }
        }
    }

 

 

再看一下LearnerHandler.java类中:

 

public class LearnerHandler extends Thread {

public void run() {
    p = queuedPackets.poll();
    ........
}
void queuePacket(QuorumPacket p) {
        queuedPackets.add(p);
    }

}

 

LearnerHandler中的处理方式是一种典型的异步处理,通过queuedPackets接受任务数据,然后线程异步进行消费处理。 因为observer可能因为网络抖动,会断开与Leader之间的链接,就会触发shutdown方法。而shutdown方法就是尝试将自己从Leader的observer句柄中移除

 

所以整个问题原因已经比较明确,removeLearnerHandler没有清理observer队列中的句柄,导致一直进行queuePacket调用,又没有异步线程进行消费,所以暴内存是迟早的事。

 

总结

特别注意:3.3.6中居然没修复这个问题,所以可升级zookeeper至3.4.5, 经过实际验证大家可放心升级(我的client 3.3.6 , server 3.4.5)

 

zookeeper 3.4和3.3的兼容性描述: http://blog.cloudera.com/blog/2011/11/apache-zookeeper-3-4-0-has-been-released/ 

 

<think>好的,用户现在问的是Kafka服务宕机了怎么排查。之前用户问过类似的问题,是关于日志目录不可用导致Broker关闭的错误,所以用户可能是在实际运维中遇到了Kafka宕机的问题,现在需要更全面的排查步骤。 首先,用户可能对Kafka的基本架构有一定了解,但可能不太清楚具体的排查流程。需要分步骤引导,从基础检查开始,逐步深入,确保用户能一步步排查问题,而不是直接跳到复杂的步骤。 然后,要考虑用户可能的场景,比如生产环境中的Kafka宕机,这时候用户需要快速恢复服务,但同时也需要找到根本原因,避免再次发生。所以回答中需要包含快速恢复的建议,比如重启Broker,但也要提醒用户注意风险。 接下来,常见的宕机原因有哪些呢?可能包括资源不足(CPU、内存、磁盘)、配置错误、依赖服务问题(比如ZooKeeper)、网络问题、日志损坏或磁盘故障,还有可能的代码缺陷或版本问题。需要把这些可能的原因都覆盖到,并提供对应的检查命令和解决方法。 另外,用户可能对某些命令不太熟悉,比如如何检查JVM内存,如何查看日志,如何测试端口连通性等,所以需要给出具体的命令示例,让用户可以直接复制使用。 还需要注意权限问题,之前用户遇到过目录权限导致的错误,所以在这次排查步骤中要再次强调检查目录权限和文件系统状态。 同时,用户可能需要监控和预防措施,避免未来再次发生类似问题,所以最后要给出预防建议,比如资源监控、配置优化、定期维护等。 最后,要提醒用户如果自行排查困难,及时联系运维团队或寻求社区支持,这样用户不会卡在某个步骤无法继续。</think>当 Kafka 服务宕机时,可按以下步骤系统化排查问题: --- ### **1. 快速检查服务状态** ```bash # 确认 Kafka 进程是否存活 ps -ef | grep kafka.Kafka # 检查端口监听(默认 9092) netstat -tulnp | grep 9092 # 查看服务启动日志(默认在 logs/server.log) tail -n 100 logs/server.log ``` --- ### **2. 常见原因及排查方向** #### **场景1:资源不足** - **检查项** ```bash # CPU/Memory top -p $(pgrep -f kafka.Kafka) # 磁盘空间 df -h /path/to/log.dirs # 文件句柄数 cat /proc/$(pgrep -f kafka.Kafka)/limits | grep "open files" ``` - **解决方案** - 清理磁盘(删除旧日志或扩容) - 调整 JVM 内存参数(`KAFKA_HEAP_OPTS="-Xmx8G -Xms8G"`) - 增大系统文件句柄限制 --- #### **场景2:ZooKeeper 连接问题** - **检查项** ```bash # 确认 ZooKeeper 集群状态 echo stat | nc zk-host 2181 # 检查 Kafka 日志中的 ZK 连接错误 grep -i "exception.*zookeeper" logs/server.log ``` - **解决方案** - 重启 ZooKeeper 服务 - 检查网络防火墙规则 - 确认 `zookeeper.connect` 配置正确 --- #### **场景3:日志文件损坏** - **检查项** ```bash # 检查日志段文件状态 ls -l /path/to/log.dirs/*/__consumer_offsets-* # 查找日志中的 Corruption 错误 grep -i "corrupt\|invalid" logs/server.log ``` - **解决方案** - 删除损坏的日志段文件(需提前备份) - 使用 `kafka-check` 工具修复 - 启用日志恢复机制:`log.flush.offset.checkpoint.interval.ms=10000` --- #### **场景4:配置错误** - **检查项** ```bash # 验证关键配置 grep -E "broker.id|listeners|log.dirs" config/server.properties # 检查端口冲突 netstat -tulnp | grep :9092 ``` - **解决方案** - 修复错误配置(如重复的 broker.id) - 检查 listeners 的 IP 绑定是否正确 - 确保 `advertised.listeners` 配置可访问 --- #### **场景5:硬件/网络故障** - **检查项** ```bash # 磁盘健康检查 smartctl -a /dev/sdX # 网络连通性测试 tcping kafka-host 9092 mtr -r zk-host ``` - **解决方案** - 更换故障磁盘 - 修复网络路由问题 - 检查交换机/网卡状态 --- ### **3. 高级诊断工具** ```bash # 检查 Controller 状态 bin/kafka-metadata-quorum.sh --describe --status # 验证副本同步 bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 # 线程堆栈分析 jstack $(pgrep -f kafka.Kafka) > kafka_thread_dump.txt ``` --- ### **4. 快速恢复建议** 1. **临时重启**(仅限紧急情况): ```bash bin/kafka-server-stop.sh sleep 10 bin/kafka-server-start.sh -daemon config/server.properties ``` 2. **优先恢复 Controller Broker** 确保 Controller 所在 Broker 先启动 --- ### **5. 预防措施** - **监控体系** 部署 Prometheus + Grafana 监控: - Broker 存活状态 - 未同步副本数(Under Replicated Partitions) - 请求队列大小(RequestQueueSize) - **配置优化** ```properties # 防止 OOM num.io.threads=16 num.network.threads=8 # 快速故障检测 zookeeper.session.timeout.ms=18000 ``` - **定期维护** 每月执行: ```bash bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 --describe ``` --- ### **6. 典型错误日志解读** | 日志关键字 | 含义 | 处理建议 | |-------------------------------|-----------------------------|----------------------------| | `NotControllerException` | Broker 丢失 Controller 身份 | 等待 Controller 选举完成 | | `LeaderElectionNotAvailable` | 副本不足无法选举 Leader | 检查 ISR 副本状态 | | `SocketTimeoutException` | 网络延迟或负载过高 | 优化网络或调整 `request.timeout.ms` | 如果问题仍无法定位,建议通过 `jmap` 生成堆转储文件,使用 Eclipse MAT 分析内存泄漏。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值