直觉上,数据包从A到B经过的路由器越少,速度就该越快。大多数时候这个假设也确实成立——物理距离更短,传播延迟更低;跳数更少,处理开销更小。
然而在真实网络里,这个假设时常失灵。有种情况会反复上演:traceroute 里路径跳数变少了,应用的响应时间反倒变长。表面看这是矛盾,实际上它恰好暴露了互联网路由机制与传输机制之间的脱节。
这不是一个需要“修复”的问题。它是一个需要在结构层面被理解的行为。
路由表的幻觉
当你运行traceroute或MTR时,你看到的只是从你到目标的一条单向路径。工具显示的是每个中间节点的IP地址和响应时间,这让它看起来像是在测量网络性能。
但它测量的是控制平面,不是数据平面。
BGP负责决定数据包如何穿越自治系统之间的边界。当BGP做出一次路由变更时——比如因为某个对等互联点的链路down掉,或因为某个ISP调整了本地优先级——路由表会收敛到一个新的拓扑状态。这个新状态可能确实包含更少的AS跳数。
但是,更少的AS跳数只是拓扑距离的缩短。它不承诺带宽充足,不承诺缓冲队列浅,也不承诺丢包率低。在新的路径上,某个拥塞的交换点或某个过载的出口路由器,就足以抵消所有路径缩短带来的好处。
更关键的是,路由变更本身可能带来瞬时的性能损伤。BGP收敛期间,路由器可能需要额外的时间来重建转发表,在此期间数据包可能被以较低优先级处理。这意味着,一个人刚刚观察到traceroute路径变短时,恰恰是路径最不稳定的时候。
在工程上,要区分控制平面信息和数据平面行为,一个直接的方法是同时收集两边的数据,而不是只看MTR的汇总输出。
# 一边持续记录路径变化,一边测量实际TCP连接建立时间 while true; do echo "=== $(date +%H:%M:%S) ===" mtr -r -c 5 -n 1.1.1.1 2>/dev/null curl -s -o /dev/null -w "TCP connect: %{time_connect}s, TTFB: %{time_starttransfer}s\n" https://cloudflare.com/cdn-cgi/trace sleep 60 done
这样做的目的不是找到“哪个跳出了问题”,而是观察路径变化与连接建立时间之间的相关性——或者说,观察它们之间惊人的缺乏相关性。路由跳数减少但time_connect上升的场景,正是路径缩短而拥塞加剧的信号。
行为链:一个常见的场景

假设用户通过狗急加速器访问一个海外应用。在某一天,用户发现响应变慢,于是运行MTR,意外地发现路径跳数比昨天还少了3跳。延迟数字显示,前几跳的RTT正常,但在某个中间节点之后突然跃升,而且波动剧烈。
这种情况通常会被直觉归因为“目标服务器变慢”。但在工程上,更值得怀疑的是路由变更引入了新的拥塞点。
可能的链条如下:
Stage 1: ISP调整了对上游提供商的BGP策略,选择了另一条更“短”的路径。这条路径可能通过一个更直接的对等互联点,减少了两跳。路由表收敛完成。从traceroute看,一切看起来更好了。
Stage 2: 这条新路径的出口节点接口带宽有限。原路径虽然跳数多,但经过了多个负载均衡的链路。新路径的所有流量现在都涌向同一个拥塞点。出口缓冲队列开始变长。
Stage 3: TCP流经这个拥塞点时,开始出现间歇性丢包。丢包触发发送端的拥塞控制机制,拥塞窗口被削减。对于用户来说,这不是持续的低速率,而是“卡顿”——某些TCP段快速通过,然后发生重传超时,应用层等待,然后再加速,再丢包。
再看第四阶段:MTR 最后一跳的丢包率并不高,可中间某一跳却显示 5% 的丢包,于是有人认定问题出在这个中间节点。事实往往相反——中间节点的控制平面会对探测包限速,丢掉的是探测包本身,这属于控制平面策略,和转发平面的拥塞毫无关系。真正发生在转发平面的丢包,位置在出口节点之后;而出口节点又可能优先处理探测包,反而把问题盖住了。
要看到转发平面的真实行为,需要发送TCP数据流而不是ICMP探测包。以下片段用hping3模拟一个带数据的TCP连接,观察SYN包的重传行为——这是比MTR丢包率更可靠的拥塞信号:
# 向目标80端口发送SYN包,如果3秒内无响应则重传 # 重传次数暗示了路径上的拥塞程度 hping3 -S -p 80 -c 20 -W 3 目标IP
如果观察到持续的SYN重传,而同时MTR显示路径跳数很少且中间节点丢包率低,那几乎可以确定问题出在转发平面的拥塞,而不是控制平面能看到的任何东西。
出口策略与拥塞的隐蔽性
ISP的出口策略是这个系统中最不透明的变量之一。一个ISP可能有多个上游提供商,同时也有多个对等互联点。它在选择出口时,不仅考虑AS路径长度,还考虑商业成本——比如通过某个对等互联点发送流量可能比通过付费的上游提供商更便宜。
这意味着,当一条路径变短时,这很可能不是因为ISP在优化性能,而是因为ISP在优化成本。
而成本驱动的选路,往往在夜间低谷时一切正常,一到峰值时段就把拥堵引了过来。于是用户看到的症状带着鲜明的时段特征:上午很顺,下午明显变慢。这种模式最容易被归因为目标服务的负载太高,或者家里 Wi-Fi 受干扰,可真正的成因多半是出口节点在特定时段撞到了容量上限。
由此还衍生出一个更难识破的误判:用户换了一个网络,问题就消失了,于是更确信’原来那个网络有问题‘。可另一个网络的运营商很可能用了完全不同的出口策略与上游,恰好绕开了那个拥堵的对等点。表面上看是换网络起了作用,本质上换的是出口路径。这个区分在工程上很重要——它决定了你接下来该往哪边走:是继续排查本地网络,还是去弄清出口路由的走向。
如果要确认这一点,需要一个不受本地ISP出口策略影响的测量点。比如从云主机向同一目标发起测量,并比较二者的TCP行为差异。
# 在本地运行 mtr -r -c 10 -n 目标IP tcptraceroute 目标IP 443 # 在云主机上同时运行相同的命令 # 如果云主机的路径跳数更多但延迟更低,说明本地ISP的短路径存在拥塞
这种比较的价值不在于找到“正确答案”,而在于建立对照基线。有了基线,才能判断某个现象是局部的还是全局的,是路径选择问题还是容量问题。
建立判断框架
要区分路径缩短带来的拥塞问题和其他类型的延迟问题,有几个信号可以参考,但没有一个信号是决定性的。
时间模式是一个起点。如果延迟恶化呈现明显的昼夜节律,且与本地用户的活跃时间吻合,那么出口拥塞的可能性更高。如果延迟恶化是突然发生且持续不变,那么路由策略变更或物理链路故障的可能性更高。
读 MTR 的中间跳,有几条经验值得记住。如果延迟不是一点点往上爬,而是在某一跳之后出现台阶式的跃升,并且后面每一跳都维持在这个高值附近,那么这一跳极可能就是出口节点,而且正在堆积队列。反之,若是渐进式的增长、每跳都加一点点,那更像是物理距离在贡献延迟。
碰到这种情况,很多人第一反应是换 DNS。有时确实见效,但起作用的机制常被说错:换 DNS 换掉的是目标服务器对应的 IP,而这个新 IP 未必还走原来的路。如果新路径绕开了那个堵住的出口节点,延迟自然就降了——这不是 DNS 变’快‘了,而是走的路不一样了。反过来,若新 IP 照样从同一个堵点出去,延迟不会有任何变化。把它叫成’DNS 问题‘其实是误判,真正的性质是路径可达性,DNS 改动只是把它触发出来而已。
还有一种情况正好相反:某些网络优化服务用自己的骨干网绕开了公共互联网上的拥堵点。用户会发现 traceroute 的路径反而变长了(隧道多出几跳),延迟却实实在在地降了。这和前面讨论的方向完全相反,道理却是同一条:拓扑上的跳数与性能之间没有必然联系——跳得少不一定更好,跳得多也不一定更差。
在判断时,一个可操作的步骤是:观察从不同源IP到同一目标的路径和延迟。如果所有源IP在通过同一个AS边界后都出现延迟跃升,那么那个AS的出口策略或对等容量很可能是主要矛盾。如果只有你的ISP出现这个问题,那么就是你本地ISP的出口选择问题。
留白
这个系统没有单一原因。网络是一个统计复用、分布式决策的概率系统。路由协议关注可达性,不关注性能。TCP关注拥塞信号,不关注路由。它们各自在自己的层面做出局部最优决策,全局行为因此涌现。
’路径变短、延迟反而变高‘,只是这类涌现行为的一个具体切面。前面给出的几条命令,目的从来不是’把问题解决掉‘,而是帮你建立观察系统的不同角度。当你能同时看到控制平面给出的路径、数据平面上 TCP 的实际表现,以及另一条网络基线作为对照时,你对’网络为什么慢‘这件事的理解,才算从猜测走向了测量。
而测量的第一步,是承认你当前看到的数字可能正在误导你。