accept_mutex是nginx防惊群的底层协调机制,默认on(1.11.3+),通过共享内存锁使worker串行抢accept权,避免多进程同时唤醒空转;仅在启用reuseport且内核≥4.5时才应设off。

accept_mutex 默认已开启,无需手动开启;关闭它反而容易引发惊群和 CPU 空转,除非你明确在用 reuseport 且内核 ≥ 4.5。
accept_mutex 是什么,为什么默认就该开着
accept_mutex 不是“可选功能”,而是 Nginx 多 worker 场景下防惊群的底层协调机制。它的作用不是提升吞吐,而是防止所有 worker 同时被唤醒去 accept() 同一个新连接——结果只有一个成功,其余返回 EAGAIN 或空转,徒增上下文切换和 CPU 消耗。
从 1.11.3 版本起,Nginx 默认启用 accept_mutex on。它通过共享内存锁让 worker 串行抢锁,抢到的才把监听 socket 加入自己的 epoll 实例;没抢到的会主动把监听 socket 从 epoll 中移除,避免被唤醒。这不是性能妥协,而是稳定性刚需。
哪些情况会误关 accept_mutex,以及后果
常见错误操作包括:
- 看到文档里写“
reuseport更高效”,就顺手加了accept_mutex off——但reuseport要求内核 ≥ 4.5 且 listen 指令显式带reuseport参数,否则关掉accept_mutex只会让惊群复现 - 遇到
accept() failed (24: Too many open files),第一反应是关accept_mutex来“缓解争抢”——实际是系统ulimit -n或worker_rlimit_nofile没调够,关锁只会让多个 worker 同时冲向已枯竭的文件描述符池 - 在低并发测试环境关掉它“想看效果”,结果压测时发现 CPU sys% 突增、
ss -s显示大量orphan连接——这正是惊群导致连接处理不及时的典型信号
accept_mutex 和 multi_accept、ngx_accept_disabled 的配合逻辑
accept_mutex 不是孤立工作的。它和两个关键机制联动:
-
multi_accept on:建议开启。它让抢到锁的 worker 在单次事件循环中尽可能多调用accept()(直到返回EAGAIN),而不是每次只收一个连接。这对短连接场景能显著降低锁争用频率 -
ngx_accept_disabled:这是个动态阈值变量,值为worker_connections / 8 - free_connection_n。当 worker 当前可用连接数低于总量的 1/8 时,该值 > 0,worker 会主动跳过抢锁,把新连接让给其他 worker——这是轻量级负载倾斜,不是等连接满了才触发
也就是说,accept_mutex on + multi_accept on 是标准搭配;而 ngx_accept_disabled 的存在,意味着你不用额外配“权重”或“忙时降权”,Nginx 已在进程级做了粗粒度自适应。
怎么确认 accept_mutex 实际生效了
不能只看配置文件写了 on,要验证运行时行为:
- 用
nginx -t确认语法无误后,检查error.log启动日志里有没有using the "epoll" event method, accept mutex on这类提示 - 压测时用
perf record -e syscalls:sys_enter_accept -p $(pgrep nginx)抓系统调用,如果看到多个 worker 进程几乎不同步地调用accept()(时间戳差几十毫秒以上),说明锁在起作用;如果多个 worker 几乎同时进入accept()且大量返回-11 (EAGAIN),说明锁可能被绕过或失效 -
stub_status显示各 worker 的Accepts数差异不大(比如 1000 vs 1020),但Handled数差异明显(比如 950 vs 870),说明连接分发是串行的,而后续请求处理是并行的——这正是accept_mutex设计的分工
真正容易被忽略的点是:accept_mutex 只作用于 accept() 阶段,对已建立连接的读写事件完全无影响。所以如果你观察到高并发下某个 worker CPU 持续 100%,但 Accepts 增速平缓,问题大概率出在业务逻辑或 upstream 延迟上,而非 accept_mutex 配置本身。











