文件描述符(fd)泄漏排查一篇就够了

生产多次遇到文件描述符(fd)泄露相关的问题, 文件描述符泄漏一般引起
的现象是文件句柄数(封面图)/tcp alloc(上图)增长。文章分为两部分介绍文件描述符相关内容,第一部分介绍文件描述基础知识,第二部分通过实际案例进行剖析。
TCP链接情况

一. 文件描述符相关基础知识

  1. 什么是文件描述符?
    内核利用文件描述符来访问文件, 打开现存文件或新建文件(建立)时,内核会
    返回一个文件描述符,读写文件也需要使用文件描述符来指定待读写的文件。所有
    执行I/O操作(包括网络socket操作)的系统调用都通过文件描述符
  2. 最大文件描述符介绍
    1. 系统最大文件描述符限制
      sysctl -a | grep fs.file-max (查看系统最大描述符)
      echo “fs.file-max=1610270” >> /etc/sysctl.conf(修改最大描述符)
      sysctl -p(立即生效)
    2. 用户级最大文件描述限制
      ulimit -n (查看用户最大描述符)
      echo “* hard nofile 65535” >> /etc/security/limits.conf
      echo “* soft nofile 65535” >> /etc/security/limits.conf
      代表所有用户,支持具体用户(优先级高,不受影响)。文件修改即生效,退出
      或打开新终端执行ulimit -n即看到修改效果
    3. 具体某个进程(PID)最大描述符
      通过cat /proc/PID/limits | grep “Max open files”
      Limit Soft Limit Hard Limit Units
      Max open files 65536 65536 files
      进程最大描述符受限与系统/用户级,以及进程本身相关代码程序限制,比如下面
      Golang代码将进程打开的最大描述符限制为10
      var rLimit syscall.Rlimit
      rLimit.Cur = 10
      if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
      panic(err)
      }

二. 文件描述符泄漏的实际案例

某个周末,ops同学在运维群反馈某核心业务应用文件描述符以及tcp alloc非常高,导致服务不可用(这块监控不到位)。业务架构同学为了排查相关问题具体原因保留了一台问题服务,当然咯,闲着无事,参与线上故障排查。从监控图看到文件描述符不断/tcp alloc 不断增长

  1. 首先考虑是否由于Socket连接建立以后未close导致,这类也是最容易排查,netstat显示的tcp连接数正常
   	netstat -tan|awk '$1~/tcp/{print $NF}'|sort|uniq -c|sort -nr
		  156  TIME_WAIT
		  141  FIN_WAIT2
		  80  ESTABLISHED
		  10  LISTEN
		  3 CLOSE_WAIT
		  2 LAST_ACK
  1. ss -s 查看大量处于closed 状态

  2. 通过lsof 查看tomcat 进程(进程4730)打开的文件描述符相关详细信息,lsof -p 4730。大量Prtocol:TCP异常描述符如下图所示:
    异常文件描述符

  3. 通过lsof 相关信息我们找不出具体由于某原因导致的,我们通过strace查看系统调用, 查看fd泄漏的具体原因 (抓取5分钟)
    strace -f -p 4730 -T -tt -o /home/futi/strace_4730.log
    -tt 在每行输出的前面,显示毫秒级别的时间
    -T 显示每次系统调用所花费的时间
    -v 对于某些相关调用,把完整的环境变量,文件stat结构等打出来。
    -f 跟踪目标进程,以及目标进程创建的所有子进程
    -e 控制要跟踪的事件和跟踪行为,比如指定要跟踪的系统调用名称
    -o 把strace的输出单独写到指定的文件
    -s 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节
    -p 指定要跟踪的进程pid, 要同时跟踪多个pid, 重复多次-p选项即可。
    tomcate多线程应用,我们需要追踪子进程运行情况,所以-f,其它参数大家看
    解析应该可以理解

  4. /home/futi/strace_4730.log,找到strace抓取这段时间内最近泄漏的fd进行分析,通过lsof -d 49959 ,可以看到出现Prtocol:TCP异常情况。下面截一小部分内容前面有大量对fd为49959打开,关闭等操作。但从4783线程操作这个fd以后strace抓取的内容未有再使用49959这个fd,且fd 不断增大,有使用大于49959的fd,所以我们可以断定是这个fd 在这块出了问题。
    操作fd:49959相关内容

  5. 从上面似乎我们找不到根本原因,《Linux环境编程:从应用到内核》有这么一段:在多线程下,可能会在fcntl调用前,就已经fork出子进程。从这点出发我们查看tomcat线程ID为4783在执行fcntl前做了哪些操作,可以看出4783线程写入了一条ERROR日志
    tomcat线程ID为4783,操作fd49959相关日志7. lsof -d 369 可以找到fd为369对应打开的文件:/data/applogs/cat/cat_20190722.log查看具体log 如下,由于连接Cat失败导致fd泄漏(由于cat上线很久了,忽略了查看cat 日志)

    [07-21 23:13:21.204] [ERROR] [ChannelManager] Error when try connecting to /host:2280Cat错误日志
    strace + lsof 能解决大部分fd泄漏的问题, 感兴趣可以公众号,定期更新后端技术

  • 5
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值