【背景】
15 年网络工程,从裸机到 K8s 都趟过。过去三年我维护一套跨机房(RTT 2~8ms)的 gRPC + HTTP/1.1 调用链,单侧新建连接速率峰值 ~800 conn/s,存量长连接 2 万上下。
每次 P99 抖动复盘,我第一个翻的就是 nstat -az 里的 TcpRetransSegs / TcpOutSegs,以及 netstat -s | grep -i retrans。绝大多数「应用侧 P99 毛刺」的根因,最终都能回溯到某段链路上一次 0.03%~0.15% 的短窗口重传——交换机 ASIC 队列溢出、光模块 FEC 纠错失败、或者对端内核 tcp_mem 压力导致 ACK 延迟。
但每次我拿着重传率去推动告警,总会遇到两种截然相反的反应:
- A 派(SRE / 应用 Owner):「重传率 0.1% 在跨机房场景下是正常水位,你拿这个告警我一天收 20 条,狼来了。」
- B 派(网络 / 内核):「> 0 就说明链路有丢包或乱序,0 才是唯一健康态,0.1% 已经够你 P99 抖 5ms 了。」
两边都有道理,但团队里到底把重传率放在哪一层,我见过三种截然不同的做法,想拉出来讨论。
【三种常见做法】
做法一:完全不管,只看应用层 重传率不进任何看板,只有应用 P99 / 5xx / 超时率作为 SLO。网络层指标(丢包、重传、RTT 抖动)归网络团队自己看,出了事再拉。
优点:告警少、不干扰;缺点:网络侧劣化(比如某条链路 FEC 纠错率从 1e-12 劣化到 1e-9)在应用 P99 上可能只体现为 0.5ms 的毛刺,根本触发不了 P99 SLO 告警,但累积起来就是慢性性能衰减。
做法二:重传率作为「参考指标」,只进看板不进告警
TcpRetransSegs/TcpOutSegs 按 5min 窗口聚合,P99 分位画在 Grafana 面板上,但不配 alert rule。只有当应用侧 SLO 被打破时,才回头翻重传率做根因分析。
优点:不产生噪音;缺点:「事后才翻」意味着你永远是被动方,而且重传窗口往往只有几秒到几十秒,5min 聚合后信号被稀释到 0.01% 以下,根因定位时根本看不出来。
做法三:重传率进 SLO 一级指标,配分级告警 比如:
- P1(30s 内 > 0.5%):立即 page on-call
- P2(5min 内 > 0.1%):Slack 频道通知
- P3(15min 内 > 0.03%):周报汇总
同时把
TcpExtTCPLostRetransmit(丢包后重传,比单纯 retrans 更严重)单独拎出来,阈值收紧一个量级。
优点:网络劣化在应用侧「还没炸」时就能被抓住;缺点:跨机房 / 跨 AZ 场景下 0.03% 可能是常态(光模块 FEC、交换机 ECN 丢弃),误报率需要反复标定。
【我的倾向与理由】
我站做法三,但有一个关键前提:阈值必须按「链路段」分,不能全局一个数。
具体做法(我们线上跑了一年多的配置,可复现):
-
采集:每 10s 一次
nstat -az(或读/proc/net/netstat),取TcpRetransSegs、TcpOutSegs、TcpExtTCPLostRetransmit、TcpExtTCPFastRetrans、TcpExtTCPSlowStartRetrans五个计数器,做差分得到 10s 窗口重传率。 -
分段:按
(src_node, dst_node, direction)三元组分桶,而不是全局聚合。跨机房段和同机房段的阈值差一个量级。 -
阈值标定(这是我们踩了半年坑才定下来的):
- 同机房(RTT < 1ms):P1 > 0.1%、P2 > 0.03%、P3 > 0.01%
- 跨机房(RTT 2~8ms):P1 > 0.3%、P2 > 0.1%、P3 > 0.03%
- 跨 AZ / 跨城(RTT > 10ms):P1 > 0.5%、P2 > 0.2%、P3 > 0.05%
TcpExtTCPLostRetransmit单独告警:同机房 > 0.01% 即 P2(因为这意味着 fast retrans 没接住,走了 RTO,延迟代价是 RTT×2 起步)
-
关联:告警里必须带上「同窗口内该链路上跑的应用 P99 变化」,否则网络团队和应用团队各说各话。我们用的是 Prometheus
join把重传率和应用 P99 按时间窗对齐,告警消息里直接贴两个数。 -
防误报:滚动重启 / 连接池重建的窗口内,
TcpOutSegs分母会突变,重传率会「虚高」——我们在采集侧加了一个outSegs < 100的过滤,低于 100 个分段的窗口不参与计算。
为什么坚持进 SLO 一级而不是「参考」:
核心原因是时间窗口。应用 P99 SLO 通常是 1min / 5min 窗口,但一次 0.05% 的重传可能只影响 2~3 个请求,在 1min 窗口里 P99 可能只从 25ms 抖到 26ms——根本触发不了 SLO 告警。但如果你把重传率单独拿出来,10s 粒度就能抓住。等应用侧 P99 告警触发时,根因窗口已经过去了 30s~2min,/proc/net/netstat 里的计数器又已经被后续流量稀释了。
为什么不能全局一个阈值:
我们实测过,同一条跨机房链路,工作日下午 3 点(交换机队列最满)和凌晨 3 点的「正常重传率」能差 3~5 倍。全局阈值要么误报满天飞(阈值太低),要么真出事时不报(阈值太高)。按链路段 + 时段分桶,误报率从 ~40% 降到了 < 5%。
【想听大家聊的】
-
你们的团队把重传率放在哪一层? 完全不看 / 看板参考 / 告警 / SLO 一级?有没有从「不看」升级到「看」的转折点(通常是一次什么事故逼的)?
-
跨机房 / 跨 AZ 场景下,你们的「正常重传率」基线是多少? 我特别想知道用华为 / 华三 / 锐捷交换机的同学,和用 Cisco / Juniper 的同学,基线有没有系统性差异——我怀疑 FEC 实现和 ECN 阈值配置会影响这个数。
-
TcpExtTCPLostRetransmit你们单独告警吗? 这个计数器比TcpRetransSegs严重得多(意味着 RTO 超时,延迟代价至少一个 RTT),但很多团队根本没注意到它的存在。 -
K8s 场景下,Pod 网络(CNI overlay / VLAN / SR-IOV)的重传率该在哪个层采集? Pod 里的
/proc/net/netstat和 Node 上的nstat,在 veth pair 场景下看到的重传数一样吗?(我的经验:不一样,veth 侧会多算一次,但具体差多少取决于 CNI 实现。) -
一个更尖锐的问题:如果重传率进了 SLO,那网络团队和应用团队的 on-call 边界怎么划?「重传率超了但应用 P99 没超」算谁的 P2?我们目前的做法是:重传率超 P2 且应用 P99 超 SLO → 应用团队主责(因为业务影响了);重传率超 P2 但应用 P99 没超 → 网络团队主责(基础设施劣化)。但这条线在「重传率 0.15%、应用 P99 从 25ms 抖到 27ms(SLO 是 30ms)」时非常模糊——你们怎么划?
欢迎贴你们的生产配置、阈值、踩坑记录,越具体越好。空谈「重传率很重要」的就不必了,我们缺的不是共识,是可复现的标定方法。