晚高峰 502 变多,error_log 里夹着一串 too many open files。第一反应是并发上去了,于是把 worker_connections 从 1024 调到 10240,reload 之后照样报错。问题在于,这个报错和连接数配置并不是同一回事。
Nginx 这一层有两个不同的上限:
worker_connections:每个 worker 可同时打开的最大连接数,属于 Nginx 自身的设置。
worker_rlimit_nofile:每个 worker 进程可打开的文件句柄数,交给操作系统执行。
两者都够,才不会报错;任意一个不够,日志里就会出现 too many open files。所以它们不是“各自独立”,而是配置上限与系统资源上限之间的约束关系。
句柄数还要算上不占连接的那些文件:访问日志、错误日志、缓存文件、rewrite 用到的临时文件,以及到上游的每一条连接。一个代理请求实际会占用两个句柄:客户端一个,后端一个。因此 worker_connections 设成 1 万,句柄至少要按 2 万上下估算,再叠加日志、缓存等固定占用并留出余量。
系统层面还有一道 fs.file-max,它决定整机最多能打开多少句柄,一般设得很大,不是常见瓶颈。真正卡住的通常是进程级软限制:ulimit -n 在很多发行版里默认是 1024。
改了 /etc/security/limits.conf 仍不生效,是这批机器上最常见的一处。这份文件由 pam_limits 在登录时读取,而 pam_limits 是 PAM 的一个模块,只作用于走登录流程的会话;systemd 启动的服务读不到里面的设置。照着帖子改完再重启服务,ulimit -n 还是原值,就是卡在这一步。
Nginx 由 systemd 拉起时,生效的是 unit 文件里的 LimitNOFILE,默认继承系统的初始值,很多发行版就是 1024。要看真实值,执行:
cat /proc/<pid>/limits
找到 open files 那一行,软限制和硬限制分两列写着,这里的数才是 Nginx 实际看到的数。
改法有两种,选一条即可:
1. 用 systemctl edit nginx 加一段 LimitNOFILE,然后 daemon-reload 再重启。只 reload 不重启,进程没有重新创建,新限制不生效。
2. 在 nginx.conf 里写 worker_rlimit_nofile,Nginx 启动时自己调用 setrlimit 抬高上限,不依赖 systemd,容器里也能用。
两条路同时写,会以较小的那个为准。容器里还要注意 docker run 的 --ulimit 参数,它和宿主机上的值是分开的;镜像里看着正常,跑起来才发现被压到 1024。
查当前用量时,可以用 ss -s 拿到 socket 总数,再用 ls /proc/<pid>/fd | wc -l 数出精确的文件句柄数;多 worker 要分别统计后汇总。两者差得远,说明有别的东西在占句柄,用 lsof 按进程排一下就能看出来。这个数要记下来,作为后续调大时的基线,别拍脑袋给值。
调大之前,先看一眼连接是不是真的在泄漏。keepalive_timeout 默认 75 秒,客户端不正常关闭的连接会挂到超时才释放。可以用:
ss -tan state time-wait
数出挂着的数量。几千条以上,就值得查客户端为什么不断开。
现在 unit 里是 LimitNOFILE=65535,worker_rlimit_nofile 写 50000,worker_connections 设成 20000,四个 worker。压测跑到 12000 并发,error_log 里不再出现那句报错,502 也随之消失。
最后要明确:句柄这项调完不会提升性能,它只是把天花板抬走。真正的容量,还是看后端能扛多少。
A5创业网 版权所有