accept_mutex是Nginx在未启用reuseport时默认启用的协调机制,仅对epoll/kqueue有效,通过共享内存CAS锁限制单worker执行accept,抑制惊群副作用而非彻底消除;它不保证连接数均衡,仅控制接入调度秩序。

accept_mutex 不是用来“解决”惊群效应的开关,而是 Nginx 在未启用 reuseport 时默认启用的、防止 worker 进程无序竞争 accept 的协调机制。它本身不消除惊群,但能有效抑制其副作用——你看到的“请求分配不均”,往往不是 accept_mutex 没起作用,而是它被绕过、禁用,或与内核能力不匹配。
accept_mutex 什么时候真正生效
这个机制只在特定条件下起作用,不是写上就管用:
- 仅对
epoll和kqueue事件模型有效;若配置了use poll;或use select;,accept_mutex被完全忽略 - Nginx 1.9.1+ 默认开启(即等价于
accept_mutex on;),旧版本如 1.4.x 必须显式写进events块,否则默认关闭 - 当
listen 80 reuseport;存在且绑定成功时,accept_mutex自动失效——此时内核接管分发,Nginx 不再抢锁 - 锁基于共享内存 + 原子操作(CAS),无系统调用阻塞;抢锁失败的 worker 立即进入事件循环处理已有连接,不会休眠等待
为什么开了 accept_mutex 还看到负载不均
accept_mutex 本身不保证各 worker 连接数一致,它只控制“谁来 accept”,不控制“连接后续怎么分”。常见误导点:
-
worker_processes auto;在 64 核机器上启 64 个进程 → 锁轮转太频繁,pidstat -w可见上下文切换(cswch/s)飙升,反而放大抖动 - 误配
multi_accept off;(默认值)→ 抢到锁的 worker 每次只 accept 一个连接就释放锁,导致锁争抢加剧,短连接洪峰下尤其明显 - 容器环境未设
net.ipv4.ip_unprivileged_port_start=0→reuseport绑定失败却无错误日志,静默回退到 mutex 模式,但你可能误以为已启用内核分发 - 第三方模块(如某些老版 upstream 模块)覆盖了
ngx_event_process_init或干扰ngx_trylock_accept_mutex()调用路径 → 机制实际被绕过
验证 accept_mutex 是否在起作用
别信配置文件里有没有那行,看运行时行为:
- 用
strace -p [worker_pid] -e trace=accept,futex观察:正常应看到少数 worker 高频调用accept(),其余 worker 几乎不调用accept(),但有零星futex等待(抢锁失败) - 压测中执行
pidstat -w -p $(pgrep nginx | head -n 3):若某 worker 的 cswch/s 持续是其他 worker 的 3–5 倍以上,说明调度仍不均,大概率是reuseport未生效或用了poll模型 - 检查监听状态:
ss -tlnp | grep :80。若多个 PID 同时 LISTEN 同一端口(且含reuseport字样),说明内核分发已启用,accept_mutex不参与;若仅一个 PID LISTEN,则依赖 mutex 协调
最易被忽略的一点:accept_mutex 是“有序抢答”,不是“负载均衡”。它解决的是 CPU 空转和调度抖动,不是连接数偏差。如果你观察到长连接场景下某个 worker 持续高负载,那大概率是客户端连接分布天然不均,或是 keepalive_timeout 设置过长导致连接滞留——这和 accept_mutex 无关,也非它该管的事。











