文件句柄耗尽是upstream连接被拒绝的常见根源,需从shell、systemd、nginx配置、worker进程四层统一调至≥65536,并合理配置worker_connections、keepalive等参数以避免fd快速耗尽。

Upstream 连接被拒绝,如果根源是文件句柄(fd)耗尽,Nginx 不会直接说“句柄不够”,而是表现为 connect() failed (24: Too many open files) 或更隐蔽的 accept() failed (24: Too many open files)、socket() failed,甚至间接引发大量 Connection refused (111) 或 502 / no live upstreams。排查必须从 fd 限制的“四层一致性”入手,缺一不可。
确认当前生效的文件句柄限制值
四个关键层级的限制必须全部 ≥65536,且彼此对齐:
-
nginx 用户的 shell 限制:
sudo -u nginx bash -c "ulimit -Sn; ulimit -Hn"—— 默认常为 1024,明显不足 - systemd 服务单元限制:
systemctl show nginx | grep LimitNOFILE—— 若显示LimitNOFILE=1024,说明被 systemd 覆盖 - Nginx 配置中的硬性声明:
worker_rlimit_nofile 65536;必须显式写在main块中 - worker 进程实际运行值:
cat /proc/$(pgrep nginx | head -2 | tail -1)/limits | grep "Max open files"—— 这才是真实生效值,必须与前三者一致
检查 Nginx 配置是否合理压榨了句柄资源
即使系统级限制调高,配置不当仍会导致 fd 快速耗尽:
-
worker_connections应设为worker_rlimit_nofile的 70%~85%,例如后者是 65536,则前者建议 50000 左右;盲目设成 65536 反而容易触发内核限制 - 每个活跃连接至少占 1 个 fd;HTTPS 场景下,SSL session 复用、OCSP stapling、上游 keepalive 连接都会额外占用,务必预留余量
- 关闭未使用的
access_log off;,或启用缓冲:access_log /path/log main buffer=64k flush=1s; - 启用 upstream keepalive:
upstream backend { keepalive 32; },避免每请求新建后端连接 - 静态服务可加
open_file_cache max=10000 inactive=30s;,复用文件句柄和元数据
验证 fd 消耗是否真由 upstream 触发
不能只看总数,要定位到 upstream 相关连接:
- 查当前 worker 进程打开的 fd 数:
lsof -p $(pgrep nginx | head -2 | tail -1) | wc -l,对比调优前后变化 - 聚焦 upstream 连接:
lsof -p $(pgrep nginx | head -2 | tail -1) | grep ":<upstream_port>" | wc -l</upstream_port>(如:8080) - 观察 error.log 是否持续出现
Too many open files,尤其集中在connect()或accept()调用处 - 若看到
connect() failed (111: Connection refused)但后端明明在监听,极可能是因为 fd 耗尽导致新 socket 创建失败,内核无法发出 SYN 包
排除其他干扰项,锁定句柄瓶颈
有些现象看似是 upstream 故障,实则是句柄不足的连锁反应:
- “no live upstreams” 可能不是后端挂了,而是因 fd 不足,健康检查请求发不出去,反复超时后被标记为 down
-
upstream timed out有时并非网络慢,而是新连接卡在SYN_SENT状态——因为没 fd 创建 socket,根本发不出 SYN - 重启 Nginx 后短暂恢复,很快又出问题:这是典型资源耗尽特征,新进程获得干净 fd 空间,但流量一上来就再次打满
- 注意模块影响:禁用
ngx_http_perl_module等重型模块,它们会在每个请求中悄悄申请额外 fd











