关键在于multi_accept与epoll/kqueue、accept_mutex off、SO_REUSEPORT及内核参数的协同:需显式use epoll、关accept_mutex、设somaxconn=65535、listen backlog=65535,并验证ss -lnt Recv-Q接近0。

要让 Worker 进程在高并发下高效、均衡地“收割”内核 listen 队列里的就绪连接,关键不是单靠 multi_accept,而是它与事件模型、锁机制、内核分发策略的协同。开启 multi_accept on 本身只是让单个 Worker 在一次循环里多 accept 几次,但能否“均衡”,取决于连接如何落到各个 Worker 上。
必须搭配 epoll 或 kqueue 才生效
multi_accept 在 select 或 poll 下完全无效。Linux 环境下务必显式声明:
-
use epoll;(即使默认也是 epoll,显式写上更可靠) - 确认内核版本 ≥ 2.6.18(epoll 支持),生产环境建议 ≥ 3.10
- 避免混用不兼容模型,例如误配
use poll;后还开启multi_accept
accept_mutex 关还是不关?看场景
默认 accept_mutex on 是为防“惊群”,但会串行化连接接收——只有一个 Worker 能抢到锁,其他空转等待。高并发时这成了瓶颈。
- 若使用
SO_REUSEPORT(Nginx ≥ 1.9.1 + Linux ≥ 3.9),应设accept_mutex off,让所有 Worker 并行监听同一端口,内核自动分流 - 若未启用
reuseport,且 Worker 数较多(如 > 8),accept_mutex on反而造成锁争用,此时仍建议关,依赖 epoll 边缘触发 +EPOLLEXCLUSIVE(Linux 4.5+)保障安全 - 关锁后,
multi_accept on才真正发挥价值:每个 Worker 在各自收到的就绪通知里,一次性收完属于自己的那批连接
内核队列与系统参数必须对齐
再好的 Nginx 配置也救不了被内核截断的连接。listen 队列溢出会导致 SYN 丢包,用户感知为超时或失败。
- 调大
net.core.somaxconn(如设为65535),它限制内核 listen 队列最大长度 - 同步调整
net.ipv4.tcp_max_syn_backlog(至少 ≥somaxconn),应对 SYN 洪峰 - Nginx 的
worker_connections值需预留余量:建议 ≥ 单 Worker 预期峰值连接 × 1.2,否则multi_accept会更快触达EMFILE错误
验证是否真正“均衡收割”
不能只看配置写了没,要观察实际行为:
- 压测时用
ss -lnt查Recv-Q:稳定在 0~2 说明队列基本无积压;持续 > 10 表明收割不及时或队列太小 - 用
strace -p $(pgrep nginx -f | head -1) -e trace=accept4 -f抓一个 Worker,在并发请求下看是否连续返回多个 fd(非阻塞模式下返回EAGAIN停止) - 对比开启前后,相同 QPS 下的
accept()系统调用次数——下降 30% 以上才算有效

















