0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个线上服务OOM的问题分享

jf_ro2CN3Fa 来源:苏三说技术 作者:苏三呀 2022-10-24 10:47 次阅读

前言

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

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

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

项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

1 案发现场

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

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

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

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

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

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

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

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

项目地址:https://gitee.com/zhijiantianya/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

2 初步定位问题

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

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

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

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

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

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

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

该功能的流程图如下:

f973d034-5341-11ed-a3b6-dac502259ad0.png

用户通过浏览器上传excel,调用文件上传接口

该接口会上传excel到文件服务器。然后将文件url,通过mq消息,发送到mq服务器。

mq消费者消费mq消息,从文件服务器中获取excel数据,做业务处理,然后把结果写入新的excel中。

mq消费者将新excel文件上传到文件服务器,然后发websocket消息通知用户。

用户收到通知结果,然后可以下载新的excel。

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

3. 打不开dump文件

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

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

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

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

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

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

f97f01b6-5341-11ed-a3b6-dac502259ad0.png

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

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

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

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

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

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

4. 进一步分析

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

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

f9992820-5341-11ed-a3b6-dac502259ad0.png

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

f9bca20a-5341-11ed-a3b6-dac502259ad0.png

目前excel的导入导出功能,大部分是基于apache的POI技术,而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,一次性创建了大量的对象。

关键代码如下:

XSSFWorkbookwb=newXSSFWorkbook(newFileInputStream(file));
XSSFSheetsheet=wb.getSheetAt(0);

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

5. 如何解决问题?

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

关键代码改动如下:

XSSFWorkbookwb=newXSSFWorkbook(newFileInputStream(file));
SXSSFWorkbookswb=newSXSSFWorkbook(wb,100);
SXSSFSheetsheet=(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,导致内存占用过多的问题。当然线程个数参数,可以根据实际情况调整。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    12

    文章

    9013

    浏览量

    85167
  • URL
    URL
    +关注

    关注

    0

    文章

    139

    浏览量

    15309
  • mat
    mat
    +关注

    关注

    0

    文章

    8

    浏览量

    9439

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

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    ESP8266从Littlefs加载文件时它崩溃了的原因?

    在我的 ESP8266 中获得了带有大量 javascript 的网络服务器的程序。 当我尝试从 Littlefs 加载文件时它崩溃了。这可能不是原因,但是,我遇到了内存不足的 OOM
    发表于 05-10 09:24

    linux内核oom机制分析

    Linux 内核有机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉。典型
    发表于 11-13 17:01 1283次阅读
    linux内核<b class='flag-5'>oom</b>机制分析

    运营商应如何打造“看到即买到”的线上服务

    为了打造“看到即买到”的线上服务能力,运营商需要找到并开放更多的线上入口,从过去的一对一点式服务升级为点对面的融合
    发表于 10-10 09:04 1976次阅读

    一个线上虚拟展馆多少钱?

    展馆也由此火爆了起来,许多展馆纷纷推出线上游览服务,运用5G、3D、VR/AR、AI等多种先进技术,结合图像、视频、语音,以第/第三视角规划动线导览,让客户身临其境体验虚拟展馆,提升获客效率。
    的头像 发表于 10-25 14:24 1109次阅读
    做<b class='flag-5'>一个</b><b class='flag-5'>线上</b>虚拟展馆多少钱?

    OOM Killer机制学习

    当系统内存不足以分配时,Linux内核会使用OOM Killer(Out-Of-Memory Killer)机制释放内存,该机制通过系列比较选择出最适合的进程并将其kill掉,从而达到保障系统稳定运行的目的。那么在内核中,
    的头像 发表于 12-19 16:17 1119次阅读

    什么是OOM机制?怎么防止进程因为OOM机制而被杀掉?

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 02-06 11:45 2771次阅读

    图解析K8S OOM和CPU节流

    使用 Kubernetes 时,内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题。
    的头像 发表于 02-15 17:17 1274次阅读

    什么是OOM机制?怎么防止进程因为OOM机制而被杀掉?

    有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
    的头像 发表于 06-21 08:59 7923次阅读
    什么是<b class='flag-5'>OOM</b>机制?怎么防止进程因为<b class='flag-5'>OOM</b>机制而被杀掉?

    OOM会导致JVM虚拟机退出吗

    OutOfMemoryError (OOM)。 这种错误是 Error 的子类,通常表示某种无法恢复的问题。 回到主题,先说下结论: OutOfMemoryError 本身不会直接导致JVM退出,但由于其
    的头像 发表于 09-30 10:14 757次阅读

    如何部署MQTT服务

    1. 选择哪种MQTT实现方案 根据前面的介绍,可以知道,要想使用MQTT,必须要有服务端。这个服务端既可以自己部署,也可以使用公有云
    的头像 发表于 11-09 15:29 1030次阅读

    jvm哪些区域会发生oom

    of Memory,OOM),本文将详细介绍 JVM 内容可能发生 OOM 的区域。OOM 是指应用程序在申请分配内存时,没有足够的内存供其使用,导致程序无法正常执行。 堆(Heap)区域: 堆是 JVM 中最大的
    的头像 发表于 12-05 11:51 1352次阅读

    Java oom异常的原因分析

    据,而栈内存用于存储方法调用和局部变量。 当程序需要使用更多内存时,会向操作系统请求更多的内存空间。如果操作系统无法分配足够的内存空间,就会导致OOM异常的发生。 导致OOM异常的原因有多种,下面将详细介绍些常见的原因。 内存
    的头像 发表于 12-05 13:43 744次阅读

    oom异常的原因和解决方法

    OOM异常的原因 OOM异常的出现通常是由于以下几个原因造成的: 1.1 内存泄漏 内存泄漏是指资源在使用完毕后没有被正确释放或回收,从而导致内存不断占用的现象。常见的内存泄漏问题包括对象未被
    的头像 发表于 12-05 13:45 6359次阅读

    Java怎么排查oom异常

    Java中的OOM(Out of Memory)异常是指当Java虚拟机的堆内存不足以容纳新的对象时抛出的异常。OOM异常是种常见的运行时异常,经常出现在长时间运行的Java应用程序或处理大数
    的头像 发表于 12-05 13:47 1209次阅读

    1.2MB数据如何吃掉10GB内存

    特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
    的头像 发表于 11-04 15:53 200次阅读
    1.2MB数据如何吃掉10GB内存