listen backlog 是什么?连接队列满了会发生什么?
简化版
listen(backlog) 用来让 TCP socket 进入监听状态,backlog 与连接队列容量有关。服务端有半连接队列和全连接队列:三次握手未完成的连接在半连接队列,握手完成但还未被 accept 的连接在全连接队列。队列满可能导致连接超时、拒绝或丢弃。
详细版
服务端调用 bind 后调用 listen,内核开始接收 TCP 连接。客户端 SYN 到来后进入半连接队列,服务端回 SYN+ACK;客户端 ACK 回来后连接进入全连接队列,应用调用 accept 才取出。
backlog 不一定直接等于两个队列的真实容量,具体受操作系统参数影响,如 Linux 的 somaxconn、tcp_max_syn_backlog。高并发下如果应用 accept 太慢、worker 被阻塞、队列太小,就可能导致连接建立慢或失败。
完整版教学
一、listen 做了什么
Socket 创建后只是一个文件描述符。服务端要先 bind 到地址端口,再 listen 进入监听状态,最后循环 accept 已建立连接。
socket -> bind -> listen -> accept -> read/write
listen 的作用是告诉内核:“这个 socket 用来接收连接。”从这一步开始,内核会替应用处理 TCP 握手队列。
二、半连接队列和全连接队列
TCP 三次握手中,服务端收到 SYN 后,连接还没完全建立,放在半连接队列。收到客户端第三次 ACK 后,连接建立完成,放到全连接队列等待应用 accept。
SYN received -> SYN queue
ESTABLISHED wait -> accept queue
accept() -> app fd
面试里常说的 backlog,通常更关心全连接队列,但真实系统还受半连接队列参数影响。
三、backlog 不是唯一上限
你传给 listen(fd, backlog) 的值会被操作系统参数截断或调整。Linux 上全连接队列上限常受 net.core.somaxconn 影响,半连接队列受 net.ipv4.tcp_max_syn_backlog 影响。
app backlog = 4096
somaxconn = 128
effective accept queue may be 128
所以调大应用 backlog 还不够,还要检查系统内核参数和框架默认值。
四、队列满了会怎样
全连接队列满时,新完成握手的连接可能被丢弃、重传、超时,或在某些配置下返回 RST。客户端看到的可能是连接慢、超时或连接被拒绝。
accept too slow -> accept queue full -> new connection waits/fails
半连接队列满时,SYN Flood 风险更明显。系统可能启用 SYN Cookie,减少半连接队列被占满的影响。
五、accept 太慢的原因
全连接队列堆积不一定是 backlog 太小,可能是应用主线程卡住、accept 线程太少、worker 处理慢、文件描述符耗尽、CPU 被打满。
| 原因 | 表现 | 排查方向 |
|---|---|---|
| accept 慢 | 连接队列积压 | 线程模型/事件循环 |
| fd 不够 | accept EMFILE | ulimit |
| CPU 高 | 延迟上升 | profile |
| SYN Flood | 半连接异常 | syn backlog/SYN Cookie |
只调 backlog 可能掩盖不了应用处理能力不足。
六、和反向代理的关系
生产中客户端常先连 Nginx、LVS 或网关,再由它们连后端服务。每一层都有自己的连接队列和超时。入口层队列没问题,不代表后端服务队列没问题。
client -> Nginx backlog -> app backlog -> worker
排查连接问题要沿链路逐层看。尤其是短连接高峰、发布重启、流量突刺时,队列容量和 accept 速度都很关键。
七、如何监控和调优
可以观察连接状态、accept 错误、SYN 重传、队列溢出指标、应用 accept 延迟。调优包括增大 backlog 和内核参数、提高 accept 能力、减少阻塞、启用连接复用和限流。
ss -lnt
netstat -s | grep listen
但调优要有指标依据。盲目把队列调很大,只会让请求排队更久,未必改善用户体验。
记忆钩子:backlog 是连接进入应用前的“候车区”,候车区太小会挤爆,服务员太慢也会挤爆。
容量评估时还要看发布和重启场景。服务刚恢复时,客户端可能集中重连,短时间连接速率远高于平时 QPS。这个峰值会直接冲击 backlog、accept 速度和 TLS 握手 CPU。
normal: 500 conn/s
after restart: 20,000 reconnects in 10s
八、常见误区与追问
- 误区:backlog 就是最大并发连接数。 它主要影响等待 accept 的连接队列,不等于已建立连接总数。
- 误区:listen backlog 调大就能解决所有连接失败。 accept 慢、fd 耗尽、CPU 高和限流都可能导致失败。
- 误区:只有应用服务有 backlog。 代理、网关、后端每层监听 socket 都有队列。
- 追问:半连接队列和全连接队列区别? 前者握手未完成,后者握手完成等待 accept。
- 追问:SYN Flood 影响哪个队列? 主要冲击半连接队列。
- 追问:accept 返回的新 fd 是什么? 已建立连接的 socket,用于后续读写。
- 追问:为什么连接有时超时不是拒绝? 队列满时内核可能丢弃或等待重传,客户端表现为超时。
九、加强记忆
listen backlog 要放在 TCP 建连流程里理解:SYN 进半连接队列,三次握手完成后进全连接队列,应用 accept 才真正拿到连接。队列满只是现象,背后可能是流量突刺、accept 慢、fd 不够或半连接攻击。回答时把两类队列、系统参数和应用处理能力串起来,就能超过背 API 的层次。