用 ss 而不是 netstat:Linux 端口与连接排查的正确姿势
netstat 已经是过去式
netstat 属于 net-tools 包,依赖读取 /proc/net/tcp 全表再做格式化,连接数上万时能明显感觉到卡顿。主流发行版早已默认提供 ss(iproute2 的一部分),它直接走 netlink 接口,只取你关心的那部分数据,速度快一个量级。新一点的系统上 netstat 甚至默认没装。
先把常用参数记住,后面所有排查都围绕这几个组合:
ss -lntp # 监听中的 TCP 端口 + 进程
ss -tnp # 已建立的 TCP 连接
ss -s # 汇总统计
ss -tan state time-wait | wc -l # 统计 TIME_WAIT 数量
-l 只看监听,-t 是 TCP,-n 不做 DNS 反解(很重要,否则 22 端口会变成 ssh,几十个 IP 反解能卡好几秒),-p 显示进程。
场景一:端口到底被谁占了
服务起不来,报 Address already in use。最直接的定位:
ss -lntp 'sport = :8080'
输出里的 users:(("java",pid=1234,fd=99)) 直接告诉你是哪个进程。相比 lsof -i:8080,ss 不需要 root 也能看到自己用户的进程,权限要求更宽松。
需要注意 0.0.0.0:8080 和 127.0.0.1:8080 是两回事:前者监听所有网卡,后者只监听回环。只监听了回环的服务,从外部访问必然不通,而 ss -lntp 的输出一眼就能看出来,不用去猜防火墙。
场景二:TIME_WAIT 堆积
短连接多的服务,ss -s 里 timewait 数量持续几万,新连接开始偶发失败。这时候要分清两件事:
TIME_WAIT 本身不是故障,它是 TCP 主动关闭方的正常状态,持续 2MSL(Linux 上固定 60 秒),作用是防止旧连接的延迟报文污染新连接。数量大只说明短连接多。
真正需要动手的是两种情况:一是端口耗尽(本地端口范围默认 32768–60999,约 2.8 万个),二是 conntrack 表被打满。前者可以通过 net.ipv4.ip_local_port_range 扩大范围,后者看 dmesg | grep conntrack。
至于 net.ipv4.tcp_tw_reuse=1,它只对主动发起连接的一方生效,且要求时间戳开启(tcp_timestamps=1,默认开)。它解决的是「作为客户端连出去」的场景,服务端被动关闭的连接不受它影响。而 tcp_tw_recycle 在 4.12 之后已经被移除,网上那些教程别再照抄了。
更根本的解法是改成长连接或连接池,从源头上减少连接建立次数。
场景三:连接队列溢出
这是最隐蔽的一类。现象是压测时 QPS 上不去、客户端偶发超时,但服务端日志干干净净,CPU 也不高。
先看两个计数:
ss -lnt
Recv-Q Send-Q
0 511 LISTEN 0 511 *:8080
nstat -az TcpExtListenOverflows TcpExtListenDrops
ss -lnt 里 Send-Q 对 LISTEN 状态表示 accept 队列上限(backlog)。如果 TcpExtListenOverflows 在持续增长,说明已完成三次握手的连接因为 accept 队列满被丢弃了,客户端会表现为「连上了但没响应」,然后超时重试。
原因通常是应用 accept 太慢——比如 Node 里在处理请求时做了同步阻塞操作,或者 Worker 数量不足。调大 somaxconn 和 backlog 只能缓解,治本还是要让 accept 循环别被阻塞。
一个实用的小脚本
按对端地址聚合,快速找出谁在刷连接:
ss -tn state established '( dport = :443 )' \
| awk 'NR>1 {split($4,a,":"); print a[1]}' \
| sort | uniq -c | sort -rn | head -20
输出就是「连接数 + 对端 IP」,限流、封禁、定位异常客户端都够用了。
小结
排查顺序建议固定成:ss -lntp 看端口状态 → ss -s 看总量 → 按 state 过滤看具体连接 → nstat 看内核计数。前三步在用户态就能完成,不需要 root,也不需要重启服务,比抓包快得多。抓包是最后手段,不是第一手段。
- 点赞
- 收藏
- 关注作者
评论(0)