前一段时间,公司同事的一个线上服务OOM的问题,我觉得挺有意思的,在这里跟大家一起分享一下。
成都创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站设计、成都做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的连山网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
我当时其实也参与了一部分问题的定位。
他们有个mq消费者服务,在某一天下午,出现OOM了,导致服务直接挂掉。
当时我们收到了很多内存的报警邮件。
发现问题之后,运维第一时间,帮他们dump了当时的内存快照,以便于开发人员好定位问题。
之后,运维重启了该服务,系统暂时恢复了正常。
大家都知道,如果出现了线上OOM问题,为了不影响用户的正常使用,最快的解决办法就是重启服务。
但重启服务治标不治本,只能临时解决一下问题,如果不找到真正的原因,难免下次在某个不经意的时间点,又会出现OOM问题。
所以,有必要定位一下具体原因。
当时运维dump下来的内存快照文件有3G多,太大了,由于公司内网限制,没办法及时给到开发这边。
没办法,只能先从日志文件下手了。
在查日志之前,我们先查看了prometheus上的服务监控。查到了当时那个mq消费者服务的内存使用情况,该服务的内存使用率一直都比较平稳,从2022-09-26 14:16:29开始,出现了一个明显的内存飙升情况。
根据以往经验总结出来的,在追查日志时,时间点是一个非常重要的过滤条件。
所以,我们当时重点排查了2022-09-26 14:16:29前后5秒钟的日志。
由于这个服务,并发量不大,在那段时间的日志量并不多。
所以,我们很快就锁定了excel文件导入导出功能。
该功能的流程图如下:
经过日志分析,时间点刚好吻合,从excel文件导入之后,mq消费者服务的内存使用率一下子飙升。
从上面分析我们得出初步的结论,线上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内存分析工具。
刚好,另一个同事的电脑上下载过OpenJDK版本的MAT内存分析工具。
把文件发给他帮忙分析了一下。
最后发现org.apache.poi.xssf.usermodel.XSSFSheet类的对象占用的内存是最多的。
目前excel的导入导出功能,大部分是基于apache的POI技术,而POI给我们提供了WorkBook接口。
常用的WorkBook接口实现有三种:
看到了这个类,可以验证之前我们通过日志分析问题,得出excel导入导出功能引起OOM的结论,是正确的。
那个引起OOM问题的功能,刚好使用了XSSFWorkbook处理excel,一次性创建了大量的对象。
关键代码如下:
XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(file));
XSSFSheet sheet = wb.getSheetAt(0);
我们通过MAT内存分析工具,已经确定OOM问题的原因了。接下来,最关键的一点是:如何解决这个问题呢?
根据我们上面的分析,既然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问题。
其实,当时我建议过使用阿里开源的EasyExcel解决OOM的问题。
但同事说,excel中有很多样式,在导出的新excel中要保留之前的样式,同时增加一列,返回导入的结果。
如果使用EasyExcel不太好处理,使用原始的Workbook更好处理一些。
但是使用mq异步导入excel文件这套方案,如果并发量大的话,任然可能会出现OOM问题,有安全隐患。
因此,有必要调整一下mq消费者。
后来,mq消费者的线程池,设置成4个线程消费,避免消费者同时处理过多的消息,读取大量的excel,导致内存占用过多的问题。当然线程个数参数,可以根据实际情况调整。
网页标题:糟了,线上服务出现OOM了
当前URL:http://www.gawzjz.com/qtweb/news19/185019.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联