句柄耗尽怎么处理
应用报 Too many open files 表示进程可用的文件描述符数量已达上限。要区分两种情况:一是并发量本来就大,需要合理上调上限;二是句柄持续增长不释放(常见于连接或文件未关闭),此时上调只是把问题推迟。排查要先看进程当前实际句柄数与各层限制,再按类型统计句柄构成。需要注意 systemd 托管的服务不读取 limits.conf,要用 drop-in 方式设置上限。
基本信息
| 进程限制 | cat /proc/<PID>/limits 查看实际生效上限 |
|---|---|
| 实际占用 | 统计 /proc/<PID>/fd 下的句柄数量 |
| 系统级 | cat /proc/sys/fs/file-nr 查看已分配与上限 |
| 会话级 | ulimit -n 查看当前会话限制 |
| 服务级 | systemd 用 drop-in 设置 LimitNOFILE |
典型使用场景
- 高并发服务运行一段时间后报句柄不足,需要判断是容量还是泄漏
- 排查连接泄漏:按句柄类型统计数量,找出异常增长的类型
- systemd 服务上调上限后仍不生效,需要确认配置位置是否正确
- 数据库或中间件调优时核对句柄上限是否符合官方建议
- 故障前采集句柄增长曲线,作为容量规划依据
- 批量巡检句柄使用情况,提前发现趋近上限的实例
判断与调整
判断与调整
cat /proc/<PID>/limits | grep -i 'open files' # 进程实际生效的上限
ls /proc/<PID>/fd | wc -l # 当前实际句柄数
cat /proc/sys/fs/file-nr # 系统级已分配与上限
ulimit -n # 当前会话上限
sudo lsof -p <PID> | awk '{print $5}' | sort | uniq -c | sort -rn | head # 按类型统计
ulimit -n 65535 # 会话级示例
# systemd 服务用 drop-in(limits.conf 对服务不生效):
# systemctl edit app-web
# [Service]
# LimitNOFILE=65535
systemctl daemon-reload && systemctl restart app-web
systemctl show app-web -p LimitNOFILE # 确认服务实际限制各层限制与查看方式
| 层级 | 查看方式 | 注意 |
|---|---|---|
| 系统级 | /proc/sys/fs/file-nr | 决定整机可用总量 |
| 会话级 | ulimit -n | 只影响当前会话及其子进程 |
| 进程级 | /proc/<PID>/limits | 进程真实生效值,以此为准 |
| 服务级 | systemctl show -p LimitNOFILE | 服务不读 limits.conf |
| 实际占用 | 统计进程 fd 目录 | 反映真实使用而非配置 |
上调上限只是止血:如果句柄随运行时间持续增长,说明存在连接或文件未释放的问题,要把最新上限再次吃满,必须按类型统计并定位泄漏点。
给 systemd 服务调上限要用 drop-in 或单元文件里的 LimitNOFILE,改 limits.conf 对该服务无效,这是「改完没变化」最常见的原因。