Linux TCP重传率高与Duplicate ACK:丢包、乱序和RTO排查指南
监控显示 TCP retransmission 飙升,抓包出现 Duplicate ACK、Fast Retransmission 或 Retransmission,应用却只感知到下载变慢、请求尾延迟升高甚至偶发超时。看到重传并不能直接判定“网络丢包”。接收端在序列空间出现缺口时会发送重复 ACK,真实丢包、路径乱序、数据包复制、抓包点遗漏以及网卡分段/合并卸载都可能产生相似迹象;如果没有足够 ACK 反馈,发送端还会等动态 RTO 到期后重传。
本文目录(32 节)
先定义重传指标口径
确认监控使用的是主机级累计计数、单位时间增量、连接级 tcp_info,还是抓包工具的分析标记。不同口径不能直接比较。主机级重传上升可能由少数大流量连接贡献,也可能因连接数增加而自然增加。
同时看每秒发送段数、重传段数、连接数、吞吐和请求延迟,计算比例与绝对值。只看一个累计 RetransSegs 会在服务运行越久时越大。
固定一条问题连接
记录五元组、进程、网络命名空间、开始/结束时间和业务请求 ID:
SRC_IP:SRC_PORT -> DST_IP:DST_PORT / TCP
PID=
NETNS=
REQUEST_ID=
在容器、NAT 和负载均衡环境中,五元组会逐段变化。分别记录应用容器、宿主机、边缘和后端看到的连接,避免把不同 NAT 后连接当成一条。
TCP为何需要重传
RFC 9293 规定 TCP 通过序列号与累计确认实现可靠字节流。发送端跟踪最早未确认序列和下一发送序列;数据或 ACK 因错误、拥塞或路径故障丢失时,发送端必须重传以保证交付。
少量重传是可靠传输机制工作的一部分。问题在于重传比例、恢复耗时和业务影响是否超出基线,而不是追求任何网络中绝对为零。
Duplicate ACK表示什么
接收端收到高于当前期望序列的报文,说明序列空间出现缺口,会立即确认仍然期望的序列号。多个后续段到达时,可能产生多个相同 ACK。
重复 ACK 说明接收端见到了乱序后的数据,但不单独证明缺失段永久丢失。原段可能稍后到达,只是走了更慢路径。
Fast Retransmit如何触发
RFC 5681 描述的快速重传通常在发送端收到三个 Duplicate ACK 后,推断某个段丢失并在 RTO 到期前重传。随后快速恢复与拥塞窗口调整参与控制发送速率。
抓包标注 Fast Retransmission 应与 ACK 序列、SACK block 和发送端 ss -ti 状态一起看。分析器的标签是启发式判断,不是内核决策日志。
RTO重传与快速重传不同
当 ACK 反馈不足,例如尾部最后一个段丢失、双向路径严重中断或窗口很小,发送端可能无法收集三个重复 ACK,只能等待 Retransmission Timeout。RFC 6298 规定 RTO 根据平滑 RTT 和 RTT 方差动态计算,并在超时后退避。
RTO 通常对尾延迟影响更明显。多个近似指数退避间隔的重传,比紧邻 Duplicate ACK 的重传更像超时恢复。
用ss查看连接级内核状态
在发送端连接仍存在时执行:
ss -tinp dst 198.51.100.10
关注 retrans、rto、rtt/rttvar、cwnd、ssthresh、bytes_sent、bytes_acked、delivery rate 和 send/receive queue。不同内核版本输出字段可能不同,应结合当前 ss 版本解释。
保存多次时间序列比单次截图更有价值。不要修改 socket 或终止连接只为方便查看。
用nstat看主机级趋势
nstat -az | grep -E 'TcpRetransSegs|TcpExtTCPTimeouts|TcpExtTCPLostRetransmit|TcpExtTCPFastRetrans'
内核版本和计数器名称可能不同。先采集基线,再在固定时间窗比较增量;容器网络命名空间也可能有独立统计。主机总量只能定位“哪台机器异常”,不能证明哪条路径丢包。
抓包必须选择正确位置
发送端应用前、宿主机物理接口、负载均衡前后和接收端看到的报文不同。尽量在两端同一时间同步抓包,并用五元组、序列号和时间对齐。
tcpdump -i eth0 -s 0 -w trace.pcap 'host 198.51.100.10 and tcp port 443'
限制目标、接口、时长和文件大小。抓包可能含业务数据,应加密保存、限制访问并按期限删除。
单端抓包为何会误报重传
抓包进程可能因 CPU、磁盘或 ring buffer 不足丢包;它没看到原始段,却看到了后续重传,于是分析器标记“重传”。检查 tcpdump 结束时的 packets dropped by kernel 和网卡丢弃计数。
只有接收端也缺少原始段,或发送端内核计数/ACK 序列支持,才能把分析标记升级为真实重传证据。
TSO/GSO/GRO/LRO的抓包伪象
发送端 TSO/GSO 可能让抓包看到尚未由网卡切分的大段,接收端 GRO/LRO 可能把多个包合并后交给内核。抓包点在卸载前后会呈现不同长度、顺序和 checksum 状态。
不要看到“bad checksum”或大于 MTU 的本机抓包就直接判定线上报文损坏。必要时在维护窗口对单接口受控关闭相关 offload 做对照,操作前评估 CPU 和吞吐影响,完成后恢复。
真实丢包的常见位置
物理链路误码、交换机端口拥塞、主机 RX/TX ring 溢出、qdisc 丢弃、防火墙队列、虚拟交换机、隧道、负载均衡和对端网卡都可能丢包。逐跳收集接口 errors/drops、队列深度和拥塞计数。
不要因为发送端只显示一次 retrans 就直接归因运营商。证据应指出原段在哪一跳仍存在、下一跳开始消失。
用ethtool检查网卡计数
ip -s link show dev eth0
ethtool -S eth0
关注 CRC、missed、no buffer、ring drop、timeout 和队列相关计数,但驱动字段名称不同。对比事件前后增量并结合厂商文档解释,不能把所有非零历史计数都当作当前故障。
qdisc和软中断饱和
出口 qdisc 丢包、单队列拥塞、CPU softirq 处理不及或 RPS/RFS/IRQ 绑定失衡,都可能在主机内造成丢包。检查 tc -s qdisc、/proc/softirqs、CPU 利用率和队列分布。
优化队列和中断亲和性需要基准与回滚。不要直接放大所有 buffer;过大队列可能把丢包变成长延迟和 bufferbloat。
接收端应用慢不是普通丢包
接收端应用不读取会让 Recv-Q 增长并通告小窗口或零窗口。发送端停发或做 zero-window probe,与拥塞丢包恢复不同。已有窗口证据时应定位应用消费、线程池或 GC,而不是调大 RTO。
抓包中的 Window Full、ZeroWindow 和 Window Update 要与重传序列分别分析。
乱序为何触发Duplicate ACK
ECMP 多路径、链路聚合、隧道、无线网络、并行处理或路由收敛可能让后发段先到。轻微乱序能由 TCP/SACK 恢复,但持续深度乱序会触发不必要快速重传并降低拥塞窗口。
对比原段是否最终到达以及重传前后的路径、TTL 和时间差。不要把所有 Duplicate ACK 都计为物理丢包率。
多路径与流哈希问题
正常 ECMP 应尽量按流保持路径。如果哈希字段变化、链路聚合逐包分担或中间封装导致同一 TCP 流跨时延差异大的路径,就会显著乱序。
检查网络设备的负载分担模式、隧道外层哈希和链路延迟。修改哈希策略影响广泛,应先在单个测试流和维护窗口验证。
SACK提供哪些线索
Selective Acknowledgment 允许接收端告诉发送端已收到哪些非连续数据块,帮助恢复多个丢失段。抓包中 SACK block 可以显示缺口边界与后续到达范围。
检查 SYN 协商是否支持 SACK,并用序列图观察。不要仅因某些安全设备历史问题就全局关闭 SACK;这会降低多丢包恢复效率。
ACK路径也可能丢包
数据从 A 到 B 正常,但 B 返回 A 的 ACK 丢失,A 仍可能重传已被 B 收到的数据。接收端会识别重复段并再次 ACK。此时只看数据方向会错误归因。
双端抓包可证明 B 已发 ACK、A 未收到;随后检查反向路由、ACL、拥塞和非对称路径。
延迟ACK不要直接当故障
TCP 接收端可延迟 ACK 以减少开销,但规范限制不能过度延迟,并对乱序段建议立即 ACK。少量 ACK 间隔不是丢包证据。
只有 ACK 延迟异常、与 RTO/吞吐下降相关且双端时间同步可信时,才检查接收主机调度、CPU 和 TCP 实现。
NAT与负载均衡的超时
中间状态设备的连接表过期、容量耗尽或 failover 不同步,会让已有 TCP 流部分方向黑洞,产生连续 RTO 重传。检查设备会话命中、超时、HA 状态和丢弃原因。
不要简单增大所有状态超时;先确认业务连接生命周期与容量,防止状态表资源耗尽。
MTU问题和普通丢包如何区分
PMTU 黑洞常表现为握手和小包正常、超过某尺寸的数据反复重传,且与 MSS/路径特定。普通拥塞丢包通常不只锁定同一尺寸边界。
已有 PMTU 专项排查时,应通过序列长度、ICMP、MSS 和不同 payload 对照确认,而不是把任何大包重传都归为 MTU。
无线与移动网络的特点
无线链路底层可能重试、切换和乱序,TCP 只看到剩余丢包与 RTT 波动。采集信号质量、无线重传、切换事件和移动网络类型,与 TCP RTO 时间对齐。
不要仅在数据中心有线环境复现移动端问题,也不要用平均 RTT 掩盖高方差和尾部超时。
RTO为什么会指数退避
RFC 6298 要求 RTO 基于 SRTT 和 RTTVAR 计算,超时重传后采用退避,避免在拥塞或断路时不断加剧网络负载。因此一次丢包可能让后续等待越来越长。
不应通过随意缩短系统 RTO 来“加快恢复”。过于激进会制造伪重传和拥塞;应修复反馈丢失、路径或应用超时设计。
应用超时与内核重传谁先发生
应用的请求超时可能短于 TCP 完整重传恢复周期。应用先取消请求或关闭 socket,用户看到超时,而内核还曾尝试恢复。反之,连接池重用、重试策略也可能放大流量并造成更多拥塞。
把应用 deadline、代理 timeout、TCP RTO 序列和业务重试对齐,避免每层都独立重试形成风暴。
不要盲目修改tcp_retries参数
Linux sysctl 中的重传阈值影响连接故障判定,降低它可能让短暂抖动变成连接断开,提高它则延长故障感知。内核参数是全局或命名空间级影响,不能替代链路修复。
任何调整都需要明确内核版本、应用 SLA、测试、监控和回滚,禁止从网络帖子复制数值直接上生产。
推荐排查顺序
- 定义指标口径和异常时间窗。
- 固定一条连接、五元组、进程和网络命名空间。
- 保存发送端
ss -ti与主机 nstat 增量。 - 同步采集双端受限抓包。
- 区分快速重传、RTO、乱序和窗口事件。
- 检查抓包丢包与 offload 伪象。
- 沿路径检查接口、队列、防火墙、NAT 和反向 ACK。
- 对齐应用 timeout/retry,完成受控修复与回归。
每一步保留同一时间基准,跨主机先确认时钟同步。
修复后的验收清单
重传绝对值与比例回到基线;P95/P99 延迟和吞吐恢复;发送端 rtt/rto/cwnd 稳定;双端抓包无持续序列缺口;接口和 qdisc 丢弃不增长;ACK 返回路径正常;应用不会因多层重试制造新负载。
还要做受控故障测试,证明监控能区分快速恢复、RTO 超时、零窗口和路径中断,而不是只发一个“网络异常”告警。
常见错误
- 把所有 Duplicate ACK 都当成丢包。
- 只用单端 Wireshark 标签下结论。
- 忽略抓包自身丢包和 TSO/GRO 影响。
- 只看主机累计 RetransSegs,不看增量与流量。
- 把 ACK 反向路径故障归因数据发送端。
- 直接放大队列或缩短 RTO。
- 修改全局 TCP sysctl 而无回滚。
- 应用、代理和客户端同时无限重试。
FAQ
三个Duplicate ACK一定表示真实丢包吗?
不一定。它们也可能由网络乱序或复制产生。发送端会按快速重传算法处理,但根因仍需双端序列和路径证据确认。
Wireshark显示Retransmission就能确定内核重传吗?
不能。分析器基于它看到的序列推断;抓包漏包、抓包位置和 offload 都可能误标。结合发送端 tcp_info/nstat 与接收端抓包验证。
Fast Retransmit和RTO哪个影响更大?
快速重传利用重复 ACK 较早恢复;RTO 要等待定时器到期并可能退避,通常对尾延迟影响更明显,但具体取决于 RTT、窗口与丢包位置。
把TCP重试次数调低能更快失败吗?
可能,但会把可恢复的短暂抖动变成连接错误,并影响所有相关连接。应先修复根因,再按 SLA 和内核文档做受控评估。
Recv-Q增长算网络丢包吗?
通常首先指向接收应用消费不及并可能触发窗口收缩。它与路径丢包是不同方向,应检查应用读取、CPU、线程和零窗口证据。
总结
Linux TCP 重传高不是单一故障类型。Duplicate ACK 可由丢包、乱序或复制引起,快速重传依赖 ACK 反馈,RTO 则在反馈不足时兜底并退避。固定单连接,结合 ss -ti、nstat、双端抓包、接口/队列计数与应用超时,排除抓包和 offload 伪象后,才能把问题定位到具体主机、链路或应用层,并在不破坏拥塞控制的前提下修复。