重新理解 TCP 三次握手与高并发调优
从握手过程出发,梳理 backlog、TIME_WAIT、SYN flood 等运维高频问题,以及对应的内核参数调优思路。
三次握手几乎是面试常客,但在运维现场,真正要命的是握手背后那些队列和状态。这篇梳理一下日常排障会用到的关键点。
握手与两个队列
客户端 SYN → 服务端 SYN+ACK → 客户端 ACK,看似三步,内核里其实维护着两个队列:
- 半连接队列(SYN queue):收到 SYN 但还没完成握手;
- 全连接队列(accept queue):握手完成、等待应用
accept()。
全连接队列满了,新完成的连接会被丢弃,表现为客户端连接超时但服务端「看起来正常」。可以这样观察:
# Recv-Q 接近 Send-Q 上限说明 accept 队列在堆积
ss -lnt | head
# 统计被 accept 队列溢出丢弃的次数
nstat -az | grep -i ListenOverflow
常见参数
# 全连接队列上限(还受 listen() 的 backlog 限制)
net.core.somaxconn = 1024
# 半连接队列
net.ipv4.tcp_max_syn_backlog = 8192
# 开启 SYN cookies 抵御 SYN flood
net.ipv4.tcp_syncookies = 1
注意:
somaxconn只是上限,应用层listen(fd, backlog)传入的值同样生效,两者取较小值。
TIME_WAIT 不是病
主动关闭方进入 TIME_WAIT 是协议的正常行为,用于确保最后的 ACK 可达并清除旧报文。短连接量大时它会堆积,但不要盲目缩短 tcp_fin_timeout 或开启有风险的 tcp_tw_recycle(新内核已移除)。更稳妥的方向是:
- 改用长连接 / 连接池,从根上减少关闭次数;
- 必要时开启
tcp_tw_reuse(仅对出站连接安全)。
排障顺序
- 先看现象在客户端还是服务端(超时 vs 拒绝);
ss -s看连接状态分布;nstat/netstat -s找丢弃计数;- 最后再动内核参数,且一次只改一个,便于归因。
调优的核心不是背参数,而是先定位是哪个队列、哪个状态出了问题,再对症下药。