18. 套接字通信与线上状态排查(热度 193 · 进阶)
Q: 服务端大量 TIME_WAIT / CLOSE_WAIT 分别说明什么?怎么排查?
A:
状态含义
TIME_WAIT 多
- 出现在主动关闭方。
- 说明该侧在大量主动关闭连接,典型:未开 keep-alive 的 HTTP 服务、反向代理主动断开、短连接客户端/服务端模型。
- 通常是正常现象,但过多会:
- 耗尽本地 ephemeral 端口;
- 占用内存(
tcp_max_tw_buckets)。
- 优化:开启连接池/长连接、
net.ipv4.tcp_tw_reuse、调整ip_local_port_range、tcp_max_tw_buckets;tcp_tw_recycle(NAT 下有坑)自 Linux 4.12 起已删除,本机/proc/sys/net/ipv4/tcp_tw_recycle已不存在,新内核上无从调起。
CLOSE_WAIT 多
- 本端已收到对端 FIN,TCP 栈已进入 CLOSE_WAIT,但应用层迟迟没有调用 close()/shutdown。
- 典型是代码 bug:
- 连接未释放;
- 线程阻塞在读/锁;
- 异常路径未关闭;
- 连接池泄漏;
- 框架/HTTP 客户端未归还连接。
- 后果:fd 与内存持续增长,最终
too many open files、accept 失败、雪崩。
口诀:
大量 CLOSE_WAIT = 应用没关 socket(代码问题);大量 TIME_WAIT = 主动关闭方短连接多(可优化)。 不看代码也能通过「谁处于 TIME_WAIT」判断谁主动断开。
设计动机 / 深层
- TCP 状态机由内核维护;应用不调用 close,内核不能擅自把 CLOSE_WAIT 变成 LAST_ACK——必须由应用确认结束。
- TIME_WAIT 属于主动关闭方的保护性状态(见第 2 题 2MSL),不应视作纯垃圾状态。
常见追问
追问 1:TIME_WAIT 多一定是故障吗?怎么优化?
- 答题要点: 通常是短连接负载高的正常结果,不是故障本身;故障信号是端口耗尽、性能下降。优化连接复用与 keep-alive,而不是简单清状态。
追问 2:CLOSE_WAIT 的修复方向?
- 答题要点: 修代码——确保所有出口关闭连接;读完 body 再还池;设置连接/读写超时;监控 fd;对下游慢响应做隔离降级,避免线程占满导致关不掉。
追问 3:还有哪些 TCP 状态值得关注?
- 答题要点:
SYN_RECV多可能 SYN 队列攻击/半连接堆积;FIN_WAIT_2异常多可能对端不回 FIN;LAST_ACK多配合 CLOSE_WAIT 反映关闭流程卡住;ESTABLISHED暴涨看连接泄漏或流量上涨。
易错点
| 易错说法 | 纠正 |
|---|---|
| 「TIME_WAIT 和 CLOSE_WAIT 都是网络问题」 | CLOSE_WAIT 多为应用 bug |
| 「CLOSE_WAIT 在主动关闭方」 | 在被动关闭方(收到 FIN 未 close) |
| 「ss/netstat 看不到进程」 | 需权限 -p,且有时需 root |
| 「把 TIME_WAIT 强行清空就没事」 | 可能丢保护、掩盖连接模型问题 |
| 「fd 多一定是泄漏」 | 也可能是正常高并发;看增长斜率与是否回收 |
与选择题对照
- 主库 150: 第 138 题(大量 CLOSE_WAIT 通常说明)、第 62/63 题(挥手与 TIME_WAIT)、第 136/137 题(ping 连通与网关)、第 38/39 题(ICMP 排障)、第 59 题(端到端)。
- 补题 16: 补-11(TCP 标志位,FIN/ACK 状态理解)。
- 口述联动: 选择题直接问 CLOSE_WAIT 含义;面试必须给排查命令与优化,而不是只背定义。
工程 / 抓包背景
- ss 系列:bash
ss -s ss -tanp ss -tan state close-wait '( sport = :443 )' ss -ti # rtt/cwnd 等 - sysctl:
tcp_tw_reuse、tcp_max_tw_buckets、net.ipv4.ip_local_port_range、net.core.somaxconn、应用层 backlog。 - Nginx:
worker_connections、keepalive上游、proxy_ignore_client_abort等与连接生命周期相关。 - 排查顺序(口诀化):
ss -tan按状态计数 →ss -tan state close-wait/state time-wait定位是哪一侧 →ss -tanp/lsof -p <pid>看 fd 是否单调上涨 → 抓包确认 FIN/ACK 时序(谁先关)→ 查代码 close/连接池归还/超时设置 → 检查ulimit -n与 backlog、sysctl。 - curl 复现: 高并发短连接压测观察 TIME_WAIT 上涨;故意不读响应/中止客户端看服务端状态。
- Java/Go 常见坑: 响应 body 未 close、连接池
borrow未return、HTTP 客户端全局连接数过小导致排队。