TCP重传不等于业务异常,需结合重传率、时延影响及双向抓包定位根因:检查RST/断连、统计重传比例、分析重传间隔与RTO关系,区分正向丢包或反向ACK丢失,并排查NAT、安全组、网卡offload等隐形干扰。

业务没报错但 tcpdump 显示大量 TCP Retransmission,说明网络层已出现丢包或延迟异常,而应用层因重传机制掩盖了问题——需从“是否真影响业务”和“为什么重传”两个维度交叉分析。
确认重传是否真实影响业务时延
TCP 自身有重传+快速重传+SACK 等机制,只要重传成功、数据最终送达,上层应用(如 HTTP、数据库客户端)可能完全无感知。但代价是:时延升高、吞吐下降、连接抖动。
- 用
tcpdump -nn -r trace.pcap 'tcp[tcpflags] & (tcp-rst|tcp-fin) != 0'检查是否有 RST 或异常断连,若有,说明连接被中断过 - 用
tshark -r trace.pcap -q -z io,stat,1,"tcp.analysis.retransmission"统计每秒重传数,结合业务 QPS 判断比例(例如:QPS=100,重传率>5% 就值得深挖) - 挑几个重传明显的流,用
tshark -r trace.pcap -Y "tcp.stream eq N and tcp.analysis.retransmission" -T fields -e frame.time_epoch -e tcp.time_delta查看重传间隔,若多次重传间隔接近 RTO(如 200ms/400ms/800ms),说明链路持续不稳定
定位重传根源:先分清是单向丢包还是双向延迟不对称
重传只发生在发送方收不到 ACK 时,不一定是路径丢包,也可能是 ACK 回不来(比如反向路径拥塞、防火墙限速、NAT 超时等)。
- 抓包时务必在客户端和服务端**同时抓**,比对同一连接的 SYN/SYN-ACK/ACK 和后续 ACK 序列:若服务端发了 ACK,客户端没收到 → 反向路径问题;若客户端发了数据,服务端没收到 → 正向路径问题
- 检查服务端
netstat -s | grep -i "retran"或ss -i输出中的retrans、retransmits字段,确认内核是否也统计到重传(排除抓包位置误导) - 用
mtr --tcp -P 443 目标IP或tcpping -x 10 目标IP 443测试端到端路径稳定性,观察是否某跳延迟突增或丢包率高
排查常见隐形干扰源
很多重传不是物理链路问题,而是中间设备策略导致:
- 云厂商安全组 / ACL / WAF:部分云平台对新建连接首包限速、或对 ACK 包做深度检测引入延迟,导致 ACK 晚到触发重传
- NAT 设备老化超时:特别是长连接空闲后,NAT 表项老化,后续 ACK 无法匹配,被丢弃 → 客户端等不到 ACK 而重传
-
网卡 offload 功能干扰:如 TSO/GSO/LRO 开启时,tcpdump 抓到的是分片前的大包,但实际线路上是小包,重传逻辑与抓包视图不一致;可用
ethtool -k eth0查看并临时关闭:sudo ethtool -K eth0 tso off gso off lro off - 时间戳选项(TCP Timestamps)异常:某些老旧中间设备会错误修改或丢弃带 timestamp 的 ACK,导致接收方忽略该 ACK,发送方误判丢包
验证业务是否真的“没超时”的方法
日志没报错 ≠ 业务没受影响。很多 SDK 默认开启重试(如 OkHttp、gRPC),一次失败自动重发,最终成功但耗时翻倍。
- 在客户端代码中开启底层网络日志(如 Java 的
-Djavax.net.debug=ssl:handshake或 Netty 的io.netty.handler.logging.LoggingHandler),看是否有 connect timeout / read timeout / write timeout 记录 - 用
bpftrace或perf trace监控系统调用:perf trace -e 'syscalls:sys_enter_connect,syscalls:sys_exit_connect,syscalls:sys_enter_sendto,syscalls:sys_exit_sendto' -p $(pidof your_app),观察 sendto 是否阻塞或返回 -EAGAIN/-EWOULDBLOCK - 对比同一请求在不同环境(如直连 vs 经 LB)下的 P99 延迟,若经 LB 后延迟明显升高且重传增多,基本可锁定 LB 或其背后网络策略










