为什么VPS各种连接、下载都很慢,罪魁祸首可能是IPv6
带宽测过、路由也查过,数据看着都正常,但VPS上敲个wget就是要愣个一两秒才开始下载,SSH连接偶尔也要卡一下,说不上哪里坏了,就是整体感觉"慢半拍"。这种笼统的慢,很多时候压根不是带宽或者CPU的问题,是系统每次新建连接的时候,先去尝试了一个根本走不通的IPv6,白白等了一截超时时间。
系统本来是有防呆设计的
现代操作系统和大部分应用,处理"域名同时解析出IPv4和IPv6两个地址"这种情况时,用的是一套叫Happy Eyeballs(RFC 8305定义)的机制——同时发起IPv4和IPv6两路连接尝试,优先偏向IPv6,但哪个先连通就用哪个,不会傻等一条线路。这个设计的初衷就是为了应对"IPv6配置得七零八落"这种普遍存在的现实情况,理论上就算你的IPv6是坏的,也只会因为要等一下IPv6失败的信号,多花大概两三百毫秒,不至于让人有明显感知。
但这套防呆机制,不是所有软件都老实实现了
问题就出在这——Happy Eyeballs是个"建议标准",不是所有客户端软件都规规矩矩照着实现。真实翻过车的例子:Node.js底层网络库undici,压根没实现这套机制,一旦域名同时解析出IPv4和IPv6,它会先死磕IPv6,等到完整的连接超时时间耗尽才肯回落到IPv4,这个等待时间可以是好几秒,远不是设计初衷里那两三百毫秒的"温和延迟"。像curl这类正确实现了Happy Eyeballs的工具,同样的网络环境下访问就完全正常,反而更容易让人误以为是"某个具体命令有问题",而不是往IPv6这个方向去想。
什么样的IPv6配置最容易踩这个坑
最麻烦的不是"完全没有IPv6",也不是"IPv6配置得很干净",而是看着有、实际走不通的这种半吊子状态:
- 有IPv6地址,但没有默认路由,包发出去了却压根不知道该往哪转发
- 防火墙默默丢弃IPv6流量而不是明确拒绝——静默丢包比直接拒绝更麻烦,因为拒绝能让客户端立刻知道这条路走不通,静默丢包只能靠等超时才能确认失败
- 服务器本身AAAA记录已经解析出去了,但IPv6网络配置其实还没真正弄好
排查思路,先确认这台机器的IPv6是不是真的能用:
1 | ip -6 addr show scope global |
如果地址显示不完整(只有link-local这种本地链路地址)、或者压根没有默认路由、或者ping不通,基本可以确定就是这个"半吊子IPv6"在拖后腿。
怎么解决
思路只有两条,选一条走到底,最忌讳的就是停在中间这个半吊子状态:要么把IPv6配置彻底修好(补上默认路由、确认防火墙没有静默丢包),要么干脆利落地关掉,让系统压根不去尝试这条走不通的路,连Happy Eyeballs那两三百毫秒的等待都省了。关闭方式具体怎么操作、有哪些容易被忽略的细节(比如lo接口漏关、NetworkManager在背后跟sysctl打架),可以看之前写的为什么改了disable_ipv6,IPv6却还是没有被真正关掉那篇,把这一步做干净了,"莫名其妙变慢"这个问题往往也就跟着消失了。
如果这台VPS本身就是IPv6 only、或者IPv6是核心需求不能一关了之,那思路就完全反过来了,得把IPv6彻底修通而不是关掉,具体排查方法可以看IPv6 VPS服务无法访问的常见原因与排查方法那篇。