老旧内核下需显式配置 accept_mutex on; use epoll; multi_accept on; 才能有效抑制惊群效应,否则易因锁争抢、上下文切换加剧导致CPU空转。

在老旧 Linux 内核(如 2.6–3.8)上,Nginx 默认不启用 accept_mutex,而内核又不支持 SO_REUSEPORT,此时“惊群效应”极易发生:所有 worker 进程被内核同时唤醒争抢 accept,仅一个成功,其余空转消耗 CPU。解决的关键不是加功能,而是让机制真正跑起来。
必须显式开启并锁定事件模型
旧版 Nginx(1.4.x、1.6.x 等)默认 accept_mutex off,必须手动写进配置:
- 在
events块中添加accept_mutex on;—— 缺失即失效 - 明确指定
use epoll;(Linux 下默认即 epoll,但显式写出可防退化) - 严禁使用
use poll;或use select;—— 此时 accept_mutex 被自动忽略 - 确保
listen指令不含reuseport(旧内核不识别,可能引发静默降级)
搭配 multi_accept 才算真正生效
只开 accept_mutex 不够:抢到锁的 worker 若每次只 accept 一个连接就释放锁,高并发下会频繁争锁、加剧上下文切换。
- 必须添加
multi_accept on;—— 让获锁 worker 循环调用 accept() 直到返回EAGAIN,一次性收尽当前就绪队列中的全部新连接 - 短连接洪峰下,吞吐提升明显,
pidstat -w中各 worker 的上下文切换(cswch/s)更均衡 - 避免写成只有
accept_mutex on;却漏掉multi_accept的配置
避开常见失效陷阱
很多“开了没用”是因隐性冲突或环境限制:
- 第三方模块(尤其老旧 upstream 或 Lua 模块)可能覆盖
ngx_event_process_init,跳过ngx_trylock_accept_mutex()调用 -
worker_processes设置远超 CPU 核心数(如 32 核启 64 个 worker),导致锁轮转过密、抖动放大 -
worker_rlimit_nofile过低,accept()失败后反复重试,干扰正常调度逻辑 - 误配
accept_mutex_delay(旧版不支持)或显式写了accept_mutex off;
用运行时行为验证是否起效
别只看配置文件,三个命令直击本质:
-
ss -tlnp | grep :80→ 应只显示一个 PID 监听端口(确认未走 reuseport,正依赖 mutex) -
strace -p $(pgrep nginx | head -1) -e trace=accept,futex 2>&1 | grep futex→ 持续看到futex(FUTEX_WAIT_PRIVATE)表示进程在等锁,机制活跃 -
pidstat -w -p $(pgrep nginx | head -n 3)→ 若某 worker 的 cswch/s 是其他 worker 的 3 倍以上,说明调度仍不均,大概率是 multi_accept 未开或事件模型错误

















