0

    糟了,线上服务出现OOM了

    2023.06.07 | admin | 123次围观

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

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

    该功能的流程图如下:

    用户通过浏览器上传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多的内存下载失败 临时文件或其所在磁盘不可写,直接打开失败了。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的导入导出功能,大部分是基于apache的POI技术,而POI给我们提供了WorkBook接口。

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

    看到了这个类,可以验证之前我们通过日志分析问题,得出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问题,后面会有专门的文章介绍,感兴趣的小伙伴可以关注一下。

    最后说一句(求关注,别白嫖我)

    如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。

    求一键三连:点赞、转发、在看。

    关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    标签: mq电子表格
    发表评论