线上系统/tmp 目录不断增长分析与总结

1、问题描述

系统配置为单核4G, web 工程配置堆2G,  /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧。

2、分析

/tmp 目录存放系统运行时产生的临时文件。在Redhat-like系统上,会定期清理/tmp目录下10天未访问的文件。这个机制保证了,linux不会像windows那样在较长时间运行后变得臃肿不堪。

清理脚本位于/etc/cron.daily/tmpwatch,内容如下,

#! /bin/sh

flags=-umc

/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \

-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \

-X ‘/tmp/hsperfdata_*‘ 10d /tmp

/usr/sbin/tmpwatch "$flags" 30d /var/tmp

for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do

if [ -d "$d" ]; then

/usr/sbin/tmpwatch "$flags" -f 30d "$d"

fi

done

本质是调用了系统命令/usr/sbin/tmpwatch 来执行对/tmp和/var/tmp目录的清理。tmpwatch 一般被用来清扫那些用来临时驻留文件的目录。

注意到,tmpwatch删除时有个排除项,/tmp/hsperfdata_*,java程序在启动时,默认会生成/tmp/hsperfdata_{USERNAME}/{PID}的文件,

这个文件会存储jvm运行的相关信息。jps\jconsole等工具的数据源就是来自于这个文件。若这个文件不存在,jps命令执行时找不到这个进程。关于这个问题,曾经有个bug相关(https://bugzilla.redhat.com/show_bug.cgi?id=527425),这个bug就是由于tmpwatch 没有排除/tmp/hsperfdata_*这个目录,导致该目录被删除,jps无法找到对应的进程。

那么/tmp通常会有哪些临时数据在这里呢?

例如,jvm启动数据、mysql的sock文件、apache默认的上传文件目录、nginx的缓存文件以及一些其他进程的临时文件。

1.查看/tmp, 通过 ls /tmp | wc -l观察, /tmp文件大约以4个/s 的速度增加,而且都是二进制文件。

2.lsof -p pid 确定tmp文件都被进程id为10791的同一个Java进程打开。

根据上面的分析,这些文件应该是该进程的临时文件,而且不断在增加,有可能是文件句柄泄露。

查看该进程的句柄图

12.15号,系统打开的句柄数量在逐步的增加,而且没有出现相对平稳的迹象,确定是句柄泄露了,这印证了我们的猜想。

下来需要进一步分析究竟是什么原因造成的句柄泄露。

查看文件内容,vi -b /tmp/filename, 包含 <</Length 2541/Filter/FlateDecode>>stream 之类的内容。猜测应该是和解码有关。

谷歌搜索关键字,确定FlateDecode是解码 PDF stream 的一个工具。查看程序中引用相关pdf的代码,如下图所示:

public static byte[] transfer(byte[] bytes, int pageNum) throws IOException {

    LOG.info("PDF合同转IMAGE开始...pageNum={}", pageNum);

    PdfDecoder decode_pdf = new PdfDecoder(true);

    decode_pdf.scaling = 1.5F;

    FontMappings.setFontReplacements();

    byte[] outbytes = new byte[0];

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    try {

        decode_pdf.openPdfArray(bytes); //bytes is byte[] array with PDF

        BufferedImage img = decode_pdf.getPageAsImage(pageNum);

        ImageIO.write(img, "jpg", out);

        outbytes = out.toByteArray();

        LOG.info("PDF合同转IMAGE成功...pageNum={}", pageNum);

    catch (Exception e) {

        LOG.error("PDF合同转IMAGE异常...pageNum={},e={}", pageNum, e);

    finally {

        out.close();

    }

    return outbytes;

}

这段代码用来将pdf转化成一个jpg的图片,使用了jpedal第三方库。

jpedal是一个开源的纯Java的PDF文档解析库,可以用来方便的查看和编辑文字和图片。

回到代码, 按照以往编码的经验,有可能是PdfDecoder没有释放资源,导致生成的临时文件一直没有释放掉。查看jpedal文档,发现的确提供了closePdfFile 关闭pdf文件的方法。

finally 块里添加

decode_pdf.flushObjectValues(true);
decode_pdf.closePdfFile();

重新发版,发现之后句柄图达到了相对平稳的状态,tmp目录也不再继续增加临时文件。

虽然问题解决了,但是还有一些困惑。临时文件怎么生成的?page fault为啥这么多?

1、临时文件到底是怎么生成的?

decode_pdf.openPdfArray(bytes)  根据传进来的字节流 打开pdf文件,

jpedal在这里做了一个优化,当pdf文件小于16k时或者alwaysCacheInMemory = -1时,直接内存缓存该pdf。

当pdf文件的大小大于16k时,会在临时目录下生成一个前缀为page,后缀为bin的二进制文件,该临时目录由系统参数 java.io.tmpdir 指定,默认在/tmp目录下。

这样,由于线上环境的pdf基本都大于16k,所以/tmp目录下就会看到不断的临时文件生成。这个临时文件命名规则为page***.bin。

2、添加closePdf文件之后,为啥问题就解决了呢?

closePdf会调用PdfReader的closePdfFile()方法,该方法根据缓存的临时文件名称删除该临时文件。

3、未关闭pdf文件,为啥会引起较多的page fault呢?

page fault 分为 minor page fault 和major page fault。

major page fault也称为hard page fault, 指需要访问的内存不在虚拟地址空间,也不在物理内存中,需要从慢速设备载入。从swap回到物理内存也是hard page fault。

minor page fault也称为soft page fault, 指需要访问的内存不在虚拟地址空间,但是在物理内存中,只需要MMU建立物理内存和虚拟地址空间的映射关系即可。 
(通常是多个进程访问同一个共享内存中的数据,可能某些进程还没有建立起映射关系,所以访问时会出现soft page fault)

正常情况下,系统也会有一些pagefault,如下图所示:

,所以pagefault和该问题没有直接关系。minflt表示从内存加载数据时每秒出现的小的错误数目,可以忽略。如果majflt较大,表示从磁盘载入内存页面,发生了swap,此时需要关注。

3、总结

我们详细的回顾了此次线上发生的问题,以及如何去定位,然后去解决问题的整个过程。

(1)问题发现,收到系统磁盘空间不足的报警。

(2)问题定位,先根据du确认是tmp目录增长过快的问题,然后根据lsof和进程句柄图确定是文件句柄泄露,再根据临时文件的文件内容,定位相关的源代码,查看源代码,确认是文件句柄资源没有正确释放。

(3)解决问题,查看api,确认是资源泄露的问题,修复代码上线。

另外,第一次写这类关于线上问题故障的文章,希望大家多多反馈。

时间: 2024-08-26 19:05:39

线上系统/tmp 目录不断增长分析与总结的相关文章

线上系统架构设计 之 【数据库篇-主主从】

双主一从架构,从服务器在本地,用于备份和研发测试.两台线上服务器进行数据库相互同步,保证数据一致性,采用xtrabackup备份数据库+脚本每天1点异地备份到从服务器. 一.对线上的一台主服务器数据进行备份,并恢复到另外两台服务器上. innobackupex --defaults-file=/wqdata/mysql/my.cnf --user=bkuser --password='123456' /wqdata/mofidbbak/fullbackup/    备份 scp -P 8022

线上服务mcelog负载异常分析处理流程

一.问题概述: Nginx服务器,HP,有冗余,其中一台服务器mcelog负载比较高,日志秒级别,已经影响了此服务器业务. tail -f /var/log/mcelog #注意看此信息是不断循环,注意看 Transaction:Memory scrubbing error MemCtrl:Corrected patrol scrub error  Erroroverflow Corrected  error #注意看其它信息 CPU16 BANK 9 MCE11 337335    MCi_M

036、糟糕!运行着的线上系统突然卡死无法访问,万恶的JVM GC!

本文是个人学习<从 0 开始带你成为JVM实战高手>内容总结,详细内容扫描二维码 1.前文回顾 前5周分析了JVM核心原理.出现GC的问题情形. 2.基于JVM运行的系统最怕什么? STW 造成的卡顿问题 3.年轻代gc到底多久一次对系统影响不大? 多久一次需要根据业务来定.年轻代gc对系统影响大不大?相对于FullGC,对系统影响是不大的 4.什么时候新生代gc对系统影响很大? 系统部署部署在大内存机器上,给你的应用分配了几十个G,同时负载很高情况下(比如每秒几万请求),这时积压的请求多,大

线上系统架构设计之 【文件系统同步篇】

此项目是一个中小型APP类应用,服务器是运营商分配的两台公网IP的虚拟机,后面无共用存储等设备.由于前端要采用负载均衡技术,为了保证数据一致性和安全性,采用两台服务器网站文件之间实时同步,外加一台远程备份用服务器,共三台.所以选择Rsync+sersync 负责主服务器到远程服务器单向(参考之前文章),UNISON+inotify实现主备服务器之间实时相互同步. 一.安装过程:yum -y install ocaml wget http://www.seas.upenn.edu/~bcpierc

031、动手实验:线上系统部署如果采用G1垃圾回收器,应该如何设置参数?

本文是个人内容总结,详细内容扫描二维码 1.前文回顾 1.G1中有新 .老.大三种Region 2.新生代回收条件:新生代Eden区满的时候 3.新生代GC仍然采用复制算法 4.控制停顿时间,对Region进行挑选回收 5.进入老年的条件: (1)熬过了一定次数的GC (2)动态年龄判定规则 (3)存活对象在Survivor放不下 2.什么时候触发新生代+老年代的混合垃圾回收? -XX:InitiatingHeapOccupancyPercent,默认值是45%,表示如果老年代占据了堆内存的45

(转)HBase工程师线上工作经验总结----HBase常见问题及分析

阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端exception,一般原因是什么?5.系统越来越慢的原因是什么?6.Hbase数据写进去,为什么会没有了,可能的原因是什么?7. regionserver发生abort,遇到最多是什么情况?8.从哪些方面可以判断HBase集群是否健康?9.为了加强HBase的安全性,你会采取哪些措施?在Tcon分布式系统测

HBase工程师线上工作经验总结----HBase常见问题及分析

阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端exception,一般原因是什么?5.系统越来越慢的原因是什么?6.Hbase数据写进去,为什么会没有了,可能的原因是什么?7. regionserver发生abort,遇到最多是什么情况?8.从哪些方面可以判断HBase集群是否健康?9.为了加强HBase的安全性,你会采取哪些措施? 在Tcon分布式系统

[转]线上GC故障解决过程记录

排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http://my.oschina.net/feichexia/blog/196575 背景说明 发生问题的系统部署在Unix上,发生问题前已经跑了两周多了. 其中我用到了Hadoop源码中的CountingBloomFilter,并将其修改成了线程安全的实现(详情见:AdjustedCountingBloo

一次线上GC故障解决过程记录

排查了三四个小时,终于解决了这个GC问题,记录解决过程于此,希望对大家有所帮助.本文假定读者已具备基本的GC常识和JVM调优知识,关于JVM调优工具使用可以查看我在同一分类下的另一篇文章: http://my.oschina.net/feichexia/blog/196575 背景说明 发生问题的系统部署在Unix上,发生问题前已经跑了两周多了. 其中我用到了Hadoop源码中的CountingBloomFilter,并将其修改成了线程安全的实现(详情见:AdjustedCountingBloo