nginx参数multi_accept和accept_mutex关系
multi_accept 是 Nginx 的一个 events 模块参数,用于控制 每次收到新连接事件时,Worker 是否一次性接收所有已就绪连接。
1 | events { |
工作原理
当内核通知 Nginx监听 socket 上有新的 TCP 连接。此时有两种行为。
1.multi_accept off(默认)
每次 epoll_wait() 返回后:
epoll_wait()
│
▼
accept() 一个连接
│
处理请求
│
再次 epoll_wait()
即:一次事件,只 accept 一个连接,如果 backlog 中还有很多连接,下次 epoll 通知再继续 accept。
2.multi_accept on
每次 epoll_wait() 返回后:
epoll_wait()
│
▼
accept()
accept()
accept()
accept()
...
直到 accept 返回 EAGAIN
也就是:把监听队列(backlog)里已经建立好的连接一次全部取完。
内部逻辑类似:
1 | while (1) { |
直到:
accept()
↓
EAGAIN
说明当前没有可接收连接了。
目的:
- 建立连接延迟更低
- 高并发突发流量时效率更高
优缺点
优点
1. 降低 epoll 唤醒次数
例如:
10000 个连接
关闭:
epoll
↓
accept1
↓
epoll
↓
accept2
可能需要很多轮。
开启:
一次 epoll
↓
accept 10000 次
减少系统调用。
2. 提高吞吐
对于:
短连接
HTTP/1.0
HTTP/1.1 keepalive 很少
或者:
L4 Proxy
TCP Proxy
效果比较明显。
3. 更快清空 backlog
backlog:
1000
如果:
multi_accept off
可能一直积压。
开启:
马上清空 backlog
可以减少连接排队时间。
缺点
最大的缺点是:一个Worker可能长时间占用 CPU 去 accept 新连接,而其他 Worker 暂时没有机会处理连接。
例如:
Worker1
accept()
accept()
accept()
accept()
...
10000 次
Worker1:
一直在 accept
Worker2:
空闲
这会导致:
- Worker 间负载不均衡
- 单个 Worker 更容易成为热点
与 reuseport关系
如果开启:
1 | listen 80 reuseport; |
每个 Worker 都有自己的监听 socket。
此时:
Worker1
Worker2
Worker3
Worker4
内核会将连接分散到各个 Worker。
即使:multi_accept on,每个 Worker只会接收属于自己监听 socket 的连接,因此负载更均衡。
与accept_mutex的关系
以前(尤其在较老版本 Linux 上):
1 | events { |
用于避免多个 Worker 同时被唤醒(惊群)。
如果:
accept_mutex on
multi_accept on
获得锁的 Worker 会一次性接收大量连接,其他 Worker 在这一轮无法接收连接,因此可能进一步加剧负载倾斜。
现代 Linux 使用 epoll 并结合 EPOLLEXCLUSIVE(以及 reuseport)后,accept_mutex 的重要性已经大幅降低。
是否建议开启?
| 场景 | 建议 |
|---|---|
| 高并发短连接(如反向代理、API 网关) | 通常可开启 |
| 长连接(WebSocket、HTTP/2、大量 Keep-Alive) | 收益较小,通常保持默认即可 |
已开启 reuseport |
可以开启,负载通常较均衡 |
未开启 reuseport,Worker 数较多 |
谨慎开启,可能导致部分 Worker 接收过多连接 |
multi_accept on 的作用可以概括为一句话:
当监听 socket 变为可读时,一个 Worker 不只接收一个新连接,而是持续调用
accept(),直到监听队列为空(accept()返回EAGAIN),以更快地处理突发连接。
它的核心价值是提高突发连接场景下的接入效率,代价是可能增加 Worker 间的负载不均衡。在现代 Linux 系统中,如果同时使用 reuseport,通常更容易兼顾吞吐量和负载均衡。