线上踩坑记:项目中一次OOM的分析定位排查过程!

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 线上踩坑记:项目中一次OOM的分析定位排查过程!


前言

前一段时间,公司同事的一个线上服务OOM的问题,我觉得挺有意思的,在这里跟大家一起分享一下。

我当时其实也参与了一部分问题的定位。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

1 案发现场

他们有个mq消费者服务,在某一天下午,出现OOM了,导致服务直接挂掉。

当时我们收到了很多内存报警邮件

发现问题之后,运维第一时间,帮他们dump了当时的内存快照,以便于开发人员好定位问题。

之后,运维重启了该服务,系统暂时恢复了正常。

大家都知道,如果出现了线上OOM问题,为了不影响用户的正常使用,最快的解决办法就是重启服务。

但重启服务治标不治本,只能临时解决一下问题,如果不找到真正的原因,难免下次在某个不经意的时间点,又会出现OOM问题。

所以,有必要定位一下具体原因。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

2 初步定位问题

当时运维dump下来的内存快照文件有3G多,太大了,由于公司内网限制,没办法及时给到开发这边。

没办法,只能先从日志文件下手了。

在查日志之前,我们先查看了prometheus上的服务监控。查到了当时那个mq消费者服务的内存使用情况,该服务的内存使用率一直都比较平稳,从2022-09-26 14:16:29开始,出现了一个明显的内存飙升情况。

根据以往经验总结出来的,在追查日志时,时间点是一个非常重要的过滤条件。

所以,我们当时重点排查了2022-09-26 14:16:29前后5秒钟的日志。

由于这个服务,并发量不大,在那段时间的日志量并不多。

所以,我们很快就锁定了excel文件导入导出功能。

该功能的流程图如下:

  1. 用户通过浏览器上传excel,调用文件上传接口。
  2. 该接口会上传excel到文件服务器。然后将文件url,通过mq消息,发送到mq服务器。
  3. mq消费者消费mq消息,从文件服务器中获取excel数据,做业务处理,然后把结果写入新的excel中。
  4. mq消费者将新excel文件上传到文件服务器,然后发websocket消息通知用户。
  5. 用户收到通知结果,然后可以下载新的excel。

经过日志分析,时间点刚好吻合,从excel文件导入之后,mq消费者服务的内存使用率一下子飙升。

3. 打不开dump文件

从上面分析我们得出初步的结论,线上mq消费者服务的OOM问题,是由于excel导入导出导致的。

于是,我们查看了相关excel文件导入导出代码,并没有发现明显的异常。

为了找到根本原因,我们不得不把内存快照解析出来。

此时,运维把内存快照已经想办法发给了相关的开发人员(我的同事)。

那位同事用电脑上安装的内存分析工具:MAT(Memory Analyzer Tool),准备打开那个内存快照文件。

但由于该文件太大,占了3G多的内存,直接打开失败了。

MemoryAnalyzer.ini文件默认支持打开的内存文件是1G,后来它将参数-xmx修改为4096m

修改之后,文件可以打开了,但打开的内容却有问题。

猛然发现,原来是JDK版本不匹配导致的。

他用的MAT工具是基于SunJDK,而我们生成环境用的OpenJDK,二者有些差异。

SunJDK采用JRL协议发布,而OpenJDK则采用GPL V2协议发布。两个协议虽然都是开放源代码的,但是在使用上的不同,GPL V2允许在商业上使用,而JRL只允许个人研究使用。

所以需要下载一个基于OpenJDK版本的MAT内存分析工具。

4. 进一步分析

刚好,另一个同事的电脑上下载过OpenJDK版本的MAT内存分析工具。

把文件发给他帮忙分析了一下。

最后发现org.apache.poi.xssf.usermodel.XSSFSheet类的对象占用的内存是最多的。

目前excel的导入导出功能,大部分是基于apachePOI技术,而POI给我们提供了WorkBook接口。

常用的WorkBook接口实现有三种:

  • HSSFWorkbook:它是早期使用最多的工具,支持Excel2003以前的版本,Excel的扩展名是.xls。只能导出65535条数据,如果超过最大记录条数会报错,但不会出现内存溢出。
  • XSSFWorkbook:它可以操作Excel2003-Excel2007之间的版本,Excel的扩展名是.xlsx。最多可以导出104w条数据,会创建大量的对象存放到内存中,可能会导致内存溢出。
  • SXSSFWorkbook:它可以操作Excel2007之后的所有版本,Excel的扩展名是.xlsx。SXSSFWorkbook是streaming版本的XSSFWorkbook,它只会保存最新的rows在内存里供查看,以前的rows都会被写入到硬盘里。用磁盘空间换内存空间,不会导致内存溢出。

看到了这个类,可以验证之前我们通过日志分析问题,得出excel导入导出功能引起OOM的结论,是正确的。

那个引起OOM问题的功能,刚好使用了XSSFWorkbook处理excel,一次性创建了大量的对象。

关键代码如下:

XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(file));
        XSSFSheet sheet = wb.getSheetAt(0);

我们通过MAT内存分析工具,已经确定OOM问题的原因了。接下来,最关键的一点是:如何解决这个问题呢?

5. 如何解决问题?

根据我们上面的分析,既然XSSFWorkbook在导入导出大excel文件时,会导致内存溢出。那么,我们改成SXSSFWorkbook不就行了?

关键代码改动如下:

XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(file));
        SXSSFWorkbook swb = new SXSSFWorkbook(wb,100);
        SXSSFSheet sheet = (SXSSFSheet) swb.createSheet("sheet1");

使用SXSSFWorkbook将XSSFWorkbook封装了一层,其中100表示excel一次读入内存的最大记录条数,excel中其余的数据将会生成临时文件保存到磁盘上。这个参数,可以根据实际需要调整。

还有一点非常重要:

sheet.flushRows();

需要在程序的结尾处加上上面的这段代码,不然生成的临时文件是空的。

这样调整之后,问题被暂时解决了。

此外,顺便说一句,在使用WorkBook接口的相关实现类时,用完之后,要记得调用close方法及时关闭喔,不然也可能会出现OOM问题。

6. 后续思考

其实,当时我建议过使用阿里开源的EasyExcel解决OOM的问题。

但同事说,excel中有很多样式,在导出的新excel中要保留之前的样式,同时增加一列,返回导入的结果。

如果使用EasyExcel不太好处理,使用原始Workbook更好处理一些。

但是使用mq异步导入excel文件这套方案,如果并发量大的话,任然可能会出现OOM问题,有安全隐患。

因此,有必要调整一下mq消费者。

后来,mq消费者的线程池,设置成4个线程消费,避免消费者同时处理过多的消息,读取大量的excel,导致内存占用过多的问题。当然线程个数参数,可以根据实际情况调整。

此外,使用阿里的arthas也可以定位线上OOM问题,后面会有专门的文章介绍,感兴趣的小伙伴可以关注一下。

image.png

相关实践学习
5分钟轻松打造应对流量洪峰的稳定商城交易系统
本实验通过SAE极速部署一个微服务电商商城,同时结合RocketMQ异步解耦、削峰填谷的能力,带大家体验面对流量洪峰仍旧稳定可靠的商城交易系统!
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
存储 缓存 监控
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
小伙伴们,有没有遇到过程序突然崩溃,然后抛出一个OutOfMemoryError的异常?这就是我们俗称的OOM,也就是内存溢出 本文来带大家学习Java OOM的三大经典场景以及解决方案,保证让你有所收获!
4162 0
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
|
Java 开发者 Spring
探索Spring Boot中的原则:约定大于配置
在软件开发领域,简化和提高开发效率一直是追求的目标。而"约定大于配置"(Convention Over Configuration)正是一种理念,旨在通过默认约定和规则来减少开发人员需要做的配置工作。在Spring Boot框架中,这一原则得到了充分应用,帮助开发者更快地构建高效的应用程序。本文将深入探讨"约定大于配置"的概念、优势以及在Spring Boot中的实践。
1759 0
|
SQL 监控 网络协议
线上故障如何快速排查?来看这套技巧大全
有哪些常见的线上故障?如何快速定位问题?本文详细总结工作中的经验,从服务器、Java应用、数据库、Redis、网络和业务六个层面分享线上故障排查的思路和技巧。较长,同学们可收藏后再看。
线上故障如何快速排查?来看这套技巧大全
|
Arthas 监控 Java
Arthas 实践——生产环境排查 CPU 飚高问题
13:40 收到我们的生产环境服务器绿版 CUP 超负载告警通知。此时心里只有一个想法,重启大法好,马上登录服务器,执行 top 发现进程 30247 和 28337 占用 CPU 为 200 多和100 多基本占用了 4 核的 3 核,整个过程大概用时 30 秒,维护群依然很平静,运营的电话也没打过来,这时候我断定,这次问题应该影响面很小,用户可能也暂时没有发现,好吧,还有时间做排查。
Arthas 实践——生产环境排查 CPU 飚高问题
|
10月前
|
消息中间件 Java 调度
一次线上服务CPU100%的排查过程
文章记录了一次线上服务CPU使用率达到100%的排查过程,通过使用top命令和jstack工具确定了导致高CPU使用的线程,并分析了Disruptor组件的不当配置是问题原因,通过修改组件的策略成功解决了问题。
235 0
|
Arthas 监控 Java
arthas使用教程
arthas使用教程
895 0
|
Java 编译器 Spring
面试突击78:@Autowired 和 @Resource 有什么区别?
面试突击78:@Autowired 和 @Resource 有什么区别?
12834 5
|
缓存 Java Nacos
图文详述Nacos服务发现源码分析
图文详述Nacos服务发现源码分析
1803 0
图文详述Nacos服务发现源码分析
|
存储 SQL Java
线上又 OOM 了 ,教你快速定位问题~
线上又 OOM 了 ,教你快速定位问题~
线上又 OOM 了 ,教你快速定位问题~
|
Arthas 监控 Java
使用阿里arthas 定位oom问题原因
1,最近我们生产服务器在做营销活动的时候突然发生oom,堆内存溢出。这个很让人头疼, 我们开发排查了好长时间,最终找到了问题的所在。这里我先买一个关子,问题的原因大家看完文章就一清二白了。
使用阿里arthas  定位oom问题原因