两个队列是怎么溢出的
内核处理新连接要经过两个队列:收到 SYN 后连接进入 SYN 队列(半连接),握手完成后移入 accept 队列等待应用调用 accept 取走。第一个队列受 tcp_max_syn_backlog 约束,第二个取应用 listen backlog 与 net.core.somaxconn 的较小值。任一队列满了,新连接就会被丢弃或重置,现象是客户端超时重试、服务端日志里什么都看不到,因为请求根本没到应用。
基本信息
| SYN 队列 | 存放已收到 SYN、尚未完成握手的连接,上限 tcp_max_syn_backlog |
|---|---|
| accept 队列 | 存放已完成握手、等待应用取走的连接,上限取 backlog 与 somaxconn 小值 |
| 溢出计数 | nstat 里的 ListenOverflows 与 ListenDrops,以及 TCPReqQFullDrop |
| 直接证据 | ss -lnt 的 LISTEN 行中 Recv-Q 大于 Send-Q 表示 accept 队列已满 |
| 防护机制 | SYN cookies 在 SYN 队列溢出时接管,避免直接丢连接 |
典型使用场景
- 服务进程活着但新连接建不上:确认是否队列溢出而非进程崩溃
- 压测时连接成功率下降:核对 backlog 与内核上限的取值关系
- 应用处理突然变慢:accept 队列先满,SYN 队列随后受影响
- 容量规划:按峰值建连速率评估 somaxconn 与 backlog 取值
- 排查无日志的失败:把证据链从内核计数补到应用侧
证据链
证据链
nstat -az | grep -iE 'ListenOverflows|ListenDrops|TCPReqQFullDrop|SyncookiesSent'
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog
ss -lnt 'sport = :80' # LISTEN 行:Recv-Q 大于 Send-Q 即 accept 队列溢出
netstat -s | grep -i listen
sysctl -w net.core.somaxconn=2048 # 临时调大,应用侧仍要同步改 backlog
ss -lnt | awk 'NR==1 || $4 ~ /:80$/' # 只看目标端口监听行的队列值计数与现象判读
| 指标或现象 | 含义 | 处置 |
|---|---|---|
| LISTEN 行 Recv-Q 大于 Send-Q | accept 队列已满,应用取走太慢 | 提高应用 backlog,优化处理速度 |
| ListenOverflows 增长 | accept 队列溢出次数 | 同步调大 somaxconn 与应用 backlog |
| ListenDrops 增长 | 新连接被丢弃 | 核对两个队列上限与突发速率 |
| SYN 队列溢出 | 半连接堆积,常见 SYN 泛洪 | 启用 SYN cookies,核查来源 |
| 客户端超时但服务端无日志 | 连接未到达应用 | 从内核计数入手,而非看应用日志 |
nginx 的 backlog 默认 511,与 net.core.somaxconn 取小值生效;后端处理慢时先满的通常是 accept 队列,而不是 SYN 队列。
net.ipv4.tcp_abort_on_overflow 置 1 会在 accept 队列溢出时直接回 RST:客户端失败得更快,但会掩盖真实容量问题,排障阶段不建议开启。