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

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 一次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实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
3月前
|
Java API Maven
【zookeeper 第五篇章】Curator 库
Curator 是 Netflix 开源的 ZooKeeper 客户端框架,简化了原生 API 的使用并提供了高级功能。可通过 Maven 添加依赖 `curator-framework` 和 `curator-recipes`。示例代码展示了如何创建 Curator 连接、配置重连策略、进行节点的 CRUD 操作以及事务处理等。例如,使用 `ExponentialBackoffRetry` 实现指数退避重试,通过 `create()` 方法创建持久节点,以及利用 `inTransaction()` 启动事务来保证多个操作的原子性。
99 0
|
3月前
|
存储 API Apache
【zookeeper 第三篇章】客户端 API
本文介绍了Apache ZooKeeper客户端的一些常用命令及其用法。首先,`create`命令用于创建不同类型的节点并为其赋值,如持久化节点、有序节点及临时节点等。通过示例展示了如何创建这些节点,并演示了创建过程中的输出结果。其次,`ls`命令用于列出指定路径下的所有子节点。接着,`set`命令用于更新节点中的数据,可以指定版本号实现乐观锁机制。
35 0
|
1月前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
31 4
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
47 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
1月前
|
分布式计算 Java Hadoop
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
Hadoop-30 ZooKeeper集群 JavaAPI 客户端 POM Java操作ZK 监听节点 监听数据变化 创建节点 删除节点
62 1
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
61 0
|
2月前
|
负载均衡 API 数据安全/隐私保护
Zookeeper的客户端-原生的API
Zookeeper的客户端-原生的API
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
4月前
|
Java
jmap 查看jvm内存大小并进行dump文件内存分析
jmap 查看jvm内存大小并进行dump文件内存分析
100 3
|
4月前
|
运维 监控 Java
(十)JVM成神路之线上故障排查、性能监控工具分析及各线上问题排错实战
经过前述九章的JVM知识学习后,咱们对于JVM的整体知识体系已经有了全面的认知。但前面的章节中,更多的是停留在理论上进行阐述,而本章节中则更多的会分析JVM的实战操作。
113 1
下一篇
无影云桌面