一次zookeeper Curator客户端导致JVM OOM问题的分析记录

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 一次zookeeper Curator客户端导致JVM OOM问题的分析记录

一次JVM OOM问题的分析记录

OOM问题发生在客户的开发环境,系统是一个监控系统,表现为先高CPU,页面极卡,最后发生OOM。问实施人员拿到Heap Dump文件。来看看到底是内存不够用溢出了,还是发生了内存泄漏。

Heap Dump

  • jdk自带的jvisualvm可以用,但是表现在我电脑上卡的不行。Dump文件接近7G。
  • jprofiler,商用。本次分析借用其试用的10天。

Classes

查看到LinkedBlockingQueue$Node的个数到达了1亿6千万的层次,占用了近4G的内存。这个类是jdk自带并发包下的阻塞队列的内部类,猜测是有人创建了一个无界队列,放在线程池里,或者其他地方使用。

Biggest Objects

  • 从这两个CuratorZookeeperClient的大小接近4G,使用了全部的JVM的大小。点来开观察层次,引用。

  • EventThread类的实例中创建的waitingEvents是导致本次OOM的元凶。这是zookeeper的客户端curator的代码,竟然会导致OOM?来查看代码一探究竟。

EventThread

class EventThread extends ZooKeeperThread {
        // 看到没有,这个线程它持有了一个无界队列
        private final LinkedBlockingQueue<Object> waitingEvents =
            new LinkedBlockingQueue<Object>();
        EventThread() {
            super(makeThreadName("-EventThread"));
            setDaemon(true);
        }
    }
  • 这个无界队列用来接收等待处理的事件,看看它的调用。

  • 可以看到,有多处进行add,也有一处进行take消费。那么大致就可以猜测,消费能力远远跟不上生产的速度时,最终就会导致OOM。刚好这个系统就是对zookeeper添加了监听,监听某个节点下的事件。
  • 来看看curator对这个事件的消费具体是如何的
@Override
        public void run() {
           try {
              isRunning = true;
              while (true) {
                 Object event = waitingEvents.take();
                 if (event == eventOfDeath) {
                    wasKilled = true;
                 } else {
                    processEvent(event);
                 }
                 if (wasKilled)
                    synchronized (waitingEvents) {
                       if (waitingEvents.isEmpty()) {
                          isRunning = false;
                          break;
                       }
                    }
              }
           } catch (InterruptedException e) {
              LOG.error("Event thread exiting due to interruption", e);
           }
            LOG.info("EventThread shut down for session: 0x{}",
                     Long.toHexString(getSessionId()));
        }
  • 在这个线程中的run方法中有个while(true)进行一直消费。本系统中,在curator将事件返回到应用后,会做一定的处理,理论上耗时不会很高,但是也架不住生产的速度过于快。
  • 事情已经趋于明朗,zookeeper上更新的数据过快,导致系统监听该节点下的变化并处理的速度都跟不上。

问题定性

本次问题是一个无界队列的消费速度跟不上生产速度导致的内存溢出。

问题解决

  • 前提
    本系统是一个监控系统,被监听的系统约定将数据按照约定格式写在zookeeper中。
  • 分析生产速度
    被监控的微服务平台(内部系统)会将一些基本数据,以及交易总笔数、成功笔数等交易汇总信息写在zookeeper上,若无交易时也会定期刷新。生产的速度就和微服务的数量、交易速度呈现一个线性的关系。
  • 分析消费速度
    监控系统的一个curator客户端的接收线程调用回调处理,处理速度有限。(具体回调代码不能贴~)
  • 解决方法解决的宗旨就是消费的速度可以跟上生产的速度。但是如何以一个系统去匹敌一个平台呢,想屁吃呢。。。毫无疑问,总是要有舍弃的!
  1. 对于监控平台,往往只关心最新的,历史总会被覆盖,所以可以丢弃一些队列中老的数据。那么问题的解决方法就有了,在curator的回调方法中,将事件扔入一个新的线程池进行处理,加快处理速度,设置有限容量的队列,并且明确拒绝策略,此处最合适的就是丢弃队列中最长时间的事件任务,因为它已经没有意义。如此一来,就不会导致OOM,即时的将curator的无界队列的任务分发到线程池里去。
  2. 不再采用监听机制,监控的时效性要求并没有那么高。可以将监听改为轮询,由监控平台主动轮询zookeeper中的节点再进行解析。

吐槽

  • 架构设计无权决定,真的不是很明白,将交易笔数汇总这种实时的数据放在zookeeper这种强一致性的分布式中间件,再让时效性以及精确性要求都不是十分苛刻的监控平台进行监控的设计。随着微服务数量、交易速率、连接数的上升,zookeeper必将面临巨大的压力。。
  • 就像redis这种高频修改的中间件上其实也有监听机制,但是基本没人去用一样(redis并不强一致性,即使我们也很少遇到那种极端情况)。

分割线


补充更新

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
4月前
|
缓存 Java 中间件
jvm性能调优实战 -55RPC调用引发的OOM故障
jvm性能调优实战 -55RPC调用引发的OOM故障
59 0
|
3月前
|
存储 缓存 Java
【Zookeeper】Apach Curator 框架源码分析:后台构造器和节点操作相关源码分析(二)【Ver 4.3.0】
【Zookeeper】Apach Curator 框架源码分析:后台构造器和节点操作相关源码分析(二)【Ver 4.3.0】
22 0
|
14天前
|
Java API Apache
ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
【4月更文挑战第11天】ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
25 11
|
22天前
|
存储
ZooKeeper客户端常用命令
ZooKeeper客户端常用命令
26 1
|
2月前
|
存储 算法 Java
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
精华推荐 | 【JVM深层系列】「GC底层调优专题」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)
57 0
|
3月前
|
存储 监控 Java
JVM内存泄漏的分析与解决方案
JVM内存泄漏的分析与解决方案
|
3月前
|
存储 监控 Java
JVM监控和分析技术在实践中可能会面临什么?
JVM监控和分析技术在实践中可能会面临什么?
|
4月前
|
安全 Java API
Zookeeper(持续更新) VIP-02 Zookeeper客户端使用与集群特性
2,/usr/local/data/zookeeper-3,/usr/local/data/zookeeper-4,在每个目录中创建文件。创建四个文件夹/usr/local/data/zookeeper-1,/usr/local/data/zookeeper-Follower:只能处理读请求,同时作为 Leader的候选节点,即如果Leader宕机,Follower节点。己对外提供服务的起始状态。E: 角色, 默认是 participant,即参与过半机制的角色,选举,事务请求过半提交,还有一个是。
|
4月前
|
运维 监控 Java
【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南
学习JVM需要一定的编程经验和计算机基础知识,适用于从事Java开发、系统架构设计、性能优化、研究学习等领域的专业人士和技术爱好者。
55 5
【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南
|
4月前
Zookeeper的客户端的命令
Zookeeper的客户端的命令
18 0