网络排查:连接、丢包、重传与 DNS
网络问题的第一件事不是抓包,而是把「连不上」「连上慢」「传得慢」分清楚。这三类的排查路径几乎没有交集:连不上看端口监听与防火墙,连上慢看 DNS 与握手,传得慢看丢包重传与带宽。分类错了,抓一上午包也抓不到重点。
先分类:三类现象
| 现象 | 典型表现 | 首查项 | 首选命令 |
|---|---|---|---|
| 连不上 | connect timeout、Connection refused | 端口监听、防火墙、conntrack | ss -lntp、nc -vz、dmesg -T |
| 连上慢 | 首字节慢、后续传输正常 | DNS、TCP 握手、TLS 握手 | dig、curl -w、tcpdump |
| 传得慢 | 小响应正常、大响应慢 | 丢包重传、带宽、拥塞窗口 | ss -ti、netstat -s、sar -n DEV |
Connection refused 说明 SYN 到了对端但被 RST 拒绝(没有进程监听,或被防火墙 reject),该去查进程;connect timeout 说明 SYN 出去之后没有回音(被 DROP 或路由不通),该去查网络。方向判别错了,后面全是无用功。
连接状态分布
先看总量,再看状态构成:
ss -s # 汇总:TCP 总数与各状态计数
ss -lntp # 监听端口及对应进程,确认服务确实在听
ss -tan state time-wait | wc -l # TIME_WAIT 数量
ss -tan state close-wait | wc -l # CLOSE_WAIT 数量,重点看这个
ss -tan state established | wc -l # 已建立连接数
| 状态 | 成因 | 是否正常 | 处置方向 |
|---|---|---|---|
| ESTABLISHED | 正常通信中 | 与并发量匹配即可 | 持续上涨要怀疑泄漏或下游阻塞 |
| TIME_WAIT | 主动关闭方在等 2MSL | 几千到几万属正常 | 短连接过多则改连接池,开 tcp_tw_reuse=1 |
| CLOSE_WAIT | 对端已关闭,本端没调 close | 超过几百就是问题 | 应用没关连接:连接池未归还、异常路径漏 close |
| SYN_RECV 大量 | 半连接队列被打满 | 不正常 | 查 SYN Flood,调 somaxconn/tcp_max_syn_backlog |
| FIN_WAIT2 长期停留 | 对端不关也不回 FIN | 少量正常 | 检查对端 close 逻辑是否有缺陷 |
CLOSE_WAIT 堆积是最好判断的一类故障,它几乎只有一种解释:应用层拿到的连接没有释放。常见来源是 HTTP 客户端响应体没读完也没关闭、连接池借出后异常路径没归还。改内核参数没用,必须改代码。
丢包与重传
重传率是判断「传得慢」的硬指标:
netstat -s | grep -iE 'retrans|timeout|reset|overflow' # 协议栈累计计数
ss -ti # 每条连接的 retrans、rtt、cwnd
ss -ti dst 10.0.0.12 # 只看某个下游
netstat -s 里重点看两项:segments retransmitted 与 segments send out 的比值,以及 listen queue of a socket overflowed。
| 观测值 | 判断 | 处置方向 |
|---|---|---|
| 重传率 < 0.1% | 正常 | 无需处理 |
| 重传率 0.1% ~ 1% | 轻度丢包,大响应开始变慢 | 查链路与中间设备限速 |
| 重传率 > 1% | 明确故障,吞吐明显下降 | 用 mtr 定位丢包跳点,交网络侧 |
ss -ti 中 retrans 持续增长且 cwnd 偏小 | 拥塞或链路丢包 | 对比本机到不同目标的结果 |
listen queue overflowed 增长 | 应用 accept 太慢或 backlog 太小 | 加大 backlog,查应用是否卡住 |
ping -c 100 -i 0.2 -s 1472 10.0.0.12 # 大包探测,1472+28=1500 不分片
mtr -rwzc 100 10.0.0.12 # 逐跳丢包与延迟,报告模式便于留证
traceroute -n -T -p 443 10.0.0.12 # TCP 模式,穿透部分 ICMP 限制
对比「本机自测」与「跨机房」的结果是分水岭:本机正常、跨机房异常,问题在链路,直接找网络组;两边都异常,问题在服务端自身。
带宽是否打满
sar -n DEV 1 10 # 每网卡收发包速率与字节数
sar -n EDEV 1 # drop/s 与 fifo/s,这两个非零就是队列被丢弃
网卡带宽打满的特征是吞吐上不去、延迟同时上涨、drop/s 非零。此时调应用参数没用,只能分流、压缩响应体或升带宽。
DNS 解析慢
「连上慢」里被忽略最多的是 DNS:
dig +stats api.example.com # 看 Query time 是否异常,正常是个位到几十毫秒
dig +trace api.example.com # 从根开始逐级解析,定位是哪一级慢
dig @8.8.8.8 api.example.com # 换公共 DNS 对比,排除本地 resolver 问题
nslookup api.example.com 8.8.8.8 # 最小验证方式,快速判断记录与解析路径是否正常
cat /etc/resolv.conf # 确认 nameserver 数量与 options
systemd-resolve --statistics # 用 systemd-resolved 的机器看命中与超时
nscd -g # 有 nscd 时看缓存命中率
| 观测值 | 判断 | 处置方向 |
|---|---|---|
Query time 毫秒级且多次稳定 | 正常 | 无需处理 |
| 首次秒级、后续毫秒级 | 缓存未命中,可接受 | 保留本地缓存 |
| 每次都是几百毫秒到数秒 | resolver 配置不当或上游不可达 | 换 resolver,缩短超时 |
resolv.conf 中 nameserver 多于 2 个 | 超时会逐个串行等待 | 精简到 2 个 |
options timeout 使用默认 5 秒 | 最坏等待可达 10 秒以上 | 改 timeout:1 attempts:2,压到 2 秒内 |
高 QPS 服务每秒数千次解析会直接打爆 resolver,应用侧还要加本地 DNS 缓存。
防火墙与 conntrack
dmesg -T | grep -i conntrack # 出现 table full, dropping packet 即为表满
conntrack -C # 当前连接跟踪条目数
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
iptables -t filter -L -n -v --line-numbers | head -30
nf_conntrack_count 长期高于 nf_conntrack_max 的 80% 就会开始随机丢包,表现是「时通时不通」,极难排查。处置:调大 nf_conntrack_max、缩短 nf_conntrack_tcp_timeout_established,或者让短连接改走连接池。
排查顺序
1. ss -lntp 服务在监听吗?端口对不对?
2. nc -vz / telnet 本机到本机通不通?先排除对端问题
3. ss -s / ss -tan 连接状态分布:CLOSE_WAIT、TIME_WAIT、SYN_RECV 各多少
4. netstat -s 重传率是否超过 1%?listen overflow 是否在涨?
5. sar -n DEV/EDEV 带宽是否打满?drop/s 是否非零?
6. dig 域名解析耗时是否正常?
7. tcpdump -i eth0 -nn port 8080 -c 100 前面都没结论才抓包
抓包是最后一步,不是第一步——它只能回答「包长什么样」,回答不了「连接为什么没关」。
处置动作
| 结论 | 动作 | 注意 |
|---|---|---|
| CLOSE_WAIT 堆积 | 修连接释放逻辑,补 finally/close | 调 tcp_max_tw_buckets 之类完全无效 |
| TIME_WAIT 过多 | 上连接池、开 tcp_tw_reuse | 不要开 tcp_tw_recycle,NAT 环境会出问题 |
| 重传率高 | 交网络侧处理,应用侧加超时与退避重试 | 重试要带抖动,否则同步化会加重拥塞 |
| 带宽打满 | 压缩响应、静态资源走 CDN、扩容 | 先确认是带宽打满而不是应用变慢 |
| DNS 慢 | 精简 resolv.conf、加本地缓存 | 容器内注意 ndots 造成的多次查询 |
| conntrack 满 | 调大上限、缩短超时 | 配合连接池减少短连接 |
巡检项
- 重传率、CLOSE_WAIT 数、conntrack 使用率进监控并设阈值。
/etc/resolv.conf只留 2 个 nameserver,配timeout:1 attempts:2。- 所有出网调用必须设置连接与读取超时,禁止无超时调用。
- 内核参数(somaxconn、backlog、tw_buckets、conntrack)纳入配置管理,不手工改。
小结:先用 refused/timeout 与三类现象把问题归类,再按监听、连接状态、重传、带宽、DNS、conntrack 的顺序逐层排除;CLOSE_WAIT 堆积查代码,重传率高查链路,抓包永远放在最后一步。