现象:进程卡顿、日志报错"Too many open files"
某天线上服务突然响应变慢,查看应用日志发现大量 java.io.IOException: Too many open files 错误。虽然重启能临时恢复,但几小时后再次出现,显然存在文件描述符泄漏。
定位:用 lsof 和 /proc 统计 FD 分布
- 确认当前进程 FD 数量 :
ls /proc/$(pgrep -x myservice)/fd | wc -l,发现已接近 4 万(系统 ulimit -n 为 65535)。 - 按类型统计 FD :
sudo lsof -p $(pgrep -x myservice) | awk '{print $5}' | sort | uniq -c | sort -rn,发现大量REG(普通文件)和sock(socket)。 - 追踪未释放的 socket 连接 :
sudo lsof -i -p $(pgrep -x myservice),发现大量CLOSE_WAIT状态的 TCP 连接,目标端口是下游 Redis 集群。 - 确认代码逻辑 :查看源码,发现每次从连接池获取连接后,在异常处理分支中缺少
finally { client.close() },导致连接未归还或关闭。
修复:补齐 finally 块 + 连接池超时配置
- 代码修复 :在所有 try-catch 后添加
finally { if (client != null) client.close(); },确保无论是否异常都能释放连接。 - 连接池优化
:设置 ``maxTotal=50``、``maxIdle=10``、``minEvictableIdleTimeMillis=30000,避免空闲连接残留。 - 监控告警
:增加 ``/proc/.../fd`` 数量监控,当超过 50000 时自动告警。
复盘:从套路到预防
文件描述符泄漏的经典模式是:打开资源 → 异常/忘记关闭 → FD 累积 → 极限触发 。排查时牢记三板斧:``ulimit -n`` 看限制 → ``lsof`` 看类型 → ``strace -e trace=open,close`` 实时追踪调用。更根本的预防措施包括:强制代码检查中要求所有 I/O 资源使用 try-with-resources(Java)或 with 语句(Python),并在 CI 中集成 FD 泄漏检测工具(如 Valgrind 的 fd 相关检查)。