中文 English

跨境 VPS 慢到能睡着?一行内核参数,把国际链路吞吐拉起 20 倍

发布时间: 2026-07-02
Network Linux BBR TCP Congestion Control 跨境 VPS 内核调优 拥塞控制 国际链路 sysctl

先说结论

你从国内访问自己的美国 VPS,明明 SSH 打字很流畅,但下载一个 2 MB 的文件要一分多钟?以为是"高峰期国际链路拥堵"?大概率不是——你只是被 Linux 默认的 CUBIC 拥塞控制"骗"了。CUBIC 把每次丢包都当成"前面堵车了",立刻踩刹车退让;而国际链路本身就有稳定 5-10% 的丢包,于是你的 TCP 连接被永远压在爬行状态。换成 Google 的 BBR 算法后,同样的物理链路、同一时刻、同样的丢包率,吞吐可以立刻涨 15-24 倍。

本文记录了一次真实的 mesh 网络排查:家庭出口 → 美国 VPS 拉取 2.3 MB 的网页,实测从 30 KB/s 一路飙到 500-1000 KB/s,用的方法总共 4 行 shell 命令。文章包含三套一键脚本(Windows 11 / Ubuntu 26.04 / macOS 26),既能人工执行,也能交给 AI Agent 自动配置。所有内网地址、主机名和公网 IP 均已脱敏。

封面:Linux BBR 让跨境链路的 TCP 吞吐从爬行加速到 500+ KB/s

图 1:BBR 让丢包严重的国际链路重新跑起来。绿色曲线是开启后,红色曲线是开启前。


一、问题背景:明明 ping 得通,为什么大文件卡到怀疑人生

事情是这样的。我在境外部署了几台 VPS,通过一个 mesh VPN 组成局域网,本地机器可以直接用私有 IP 访问这些机器上的服务。日常 SSH、curl 一下网页首页、拉个几十 KB 的 JSON API,都秒回,没有任何异常。

直到有一天,我尝试打开其中一台机器上一个管理后台的完整页面(一个 2.3 MB 的 HTML),浏览器转了整整一分钟才把内容显示出来。

我第一反应是:是不是这台机器挂了? 于是登上 mesh 网关,从多个位置分别测:

不同大小的请求延迟看起来是"体验不同",但真正的瓶颈只有一个:大文件传输的实际吞吐。这就把矛头指向了 TCP 层。


二、问题表现:三个数据一对比就露馅了

我又在家里的 mesh 网关(在国内家宽下)直接跑同样的测试,四个方向的表现如下:

路径 请求 耗时 速率
家庭网关 → 美国 VPS A 2.3 MB 页面 79 秒 29 KB/s
家庭网关 → 美国 VPS B 2.3 MB 页面 90 秒 24 KB/s
家庭网关 → 美国 VPS C 2.3 MB 页面 84 秒 28 KB/s
VPS A → VPS B(美国内部) 2.3 MB 页面 0.5 秒 4.6 MB/s

三个国际方向速率极其一致地卡在 24-29 KB/s。这个数据在过去 5 年里我看过太多次了——它是"国内到境外链路吞吐被 CUBIC 压死"的经典特征值

底层公网 ICMP 测试也印证了这点:

--- 96.x.x.x ping statistics ---
30 packets transmitted, 27 received, 10% packet loss, time 5824ms
rtt min/avg/max/mdev = 166.334/167.062/168.315/0.457 ms

RTT 相当稳定(167ms),但丢包率 10%。10% 听起来不多,但对 TCP 的 CUBIC 拥塞控制来说,这个数字足以让它把连接掐死在墙角。

真实截图:开启 BBR 之前,同一条链路三次测试都卡在 30 KB/s 附近

图 2:改动之前的真实测试记录。三台 VPS 各测一次,全部卡在 30 KB/s 上下。默认的 cubic 拥塞控制就是拖后腿的那个变量。


三、问题分析:TCP 为什么这么怕丢包

要理解这个问题,得先搞清楚一件事:互联网上 TCP 的"速度"不是你想跑多快就跑多快的,它有一个内置的"礼让"机制。

用生活比喻讲清楚

想象你开着一辆快递车,在一条你不熟的高速公路上跑。你不知道前方拥不拥堵,只能通过反馈猜——如果发现某个包裹(数据包)在路上被丢了,你会怎么想?

大多数人的反应是"前面是不是堵了?我先慢点开吧"。这就是 CUBIC(Linux 默认拥塞控制算法)的思维方式

一遇到丢包,立即把发送速度砍到一半,然后小心翼翼地慢慢加回来。

在国内正常的家庭宽带上,这个策略完全合理,因为家里到路由器几乎不会有随机丢包——所以一旦有丢包,往往真的是链路堵了。但在国际链路上就变成一个灾难性的误判

CUBIC 和 BBR 在丢包链路上的行为对比

图 3:同一条 10% 丢包的国际链路上,CUBIC 是锯齿状的爬行曲线,BBR 是稳定的高吞吐曲线。差别不是"一点点",而是数量级

BBR 是怎么想的?

Google 在 2016 年提出、2017 年合入 Linux 4.9 主线的 BBR (Bottleneck Bandwidth and Round-trip time) 算法换了一种完全不同的思路:

不看丢包,直接测"这条链路一秒能到多少字节、最快 RTT 是多少",然后按照这两个数字之间的乘积(BDP)来发数据。

用比喻的话,BBR 就像一位见多识广的老司机:“这条路我熟,撒的小坑(丢包)不代表堵车,我看后车灯(RTT 上升)才判断真堵。”

生活化比喻:CUBIC 是保守派老实人,BBR 是灵活派老司机

图 4:同样一条路,CUBIC 见到坑洼就刹车,BBR 只看整体是否堵。表下的三种场景数据都是真实实测。

关键区别在遇到丢包时的反应

场景 CUBIC 的反应 BBR 的反应
RTT 稳定 + 有丢包 ❌ 以为堵,速率减半 ✅ 忽略,保持发送速率
RTT 突然拉长 + 有丢包 减速 ✅ 减速(真堵车了)
RTT 稳定 + 零丢包 缓慢加速 ✅ 主动"探测"更大带宽

对国际链路来说,第一种场景太常见了。“背景丢包"不是拥塞,只是链路物理层的正常波动,BBR 直接忽视这类噪声,才能稳定跑满带宽。

会不会有副作用?

有几个常被问到的点:

  1. BBR 会不会对邻居不公平? 会,稍微。BBR 相比 CUBIC 更"敢抢带宽”(因为它不会因为对方的流量导致轻微丢包就退让)。在你独享 VPS、独享公网 IP 的场景下没影响;在共享物理机的极端情况下,可能让隔壁 CUBIC 用户的连接稍慢一点。

  2. 对局域网、零丢包场景有帮助吗? 基本没有。此时 CUBIC 也能跑满带宽,BBR 只是持平。BBR 只在丢包链路上出奇迹。

  3. BBR 只影响 TCP,UDP 不受影响。 如果你走的是 WireGuard、Easytier、Hysteria2 这类 UDP 隧道,隧道本身不受 BBR 加速。但隧道内部承载的 TCP 流量(HTTP、SSH、git、rsync)会全面加速——这正是最常见的实际场景。


四、问题根因总结

把整个链条拉通梳理一下:

状态 是否是根因
物理层:国际光缆到目标机房 稳定丢包 6-10% 无法治愈的客观现实
传输层 TCP:默认 CUBIC 算法 把每次丢包当拥塞信号 就是它!
应用层 HTTP:Nginx / Caddy 等 完全正常 不是问题
业务层:CLIProxyAPI / Web Panel 完全正常 不是问题

根因只有一个:Linux 默认使用 CUBIC 拥塞控制,与国际链路的丢包特性天然不匹配

好消息是——修它只需要 3 行 shell。


五、如何解决:一键脚本(三大操作系统)

前置检查:内核版本

BBR 需要 Linux 内核 ≥ 4.9。这个门槛现在早就不是门槛:

Windows 11 和 macOS 26 是客户端角色(对端是 Linux VPS),本文的重点是在 Linux 服务器侧启用 BBR。客户端本身也提供了一键脚本,用于诊断链路质量和验证服务器侧配置是否生效。

真实截图:先看内核,再加载 tcp_bbr 模块,最后验证

图 5:整个"预检"过程只需要 3 条命令,出问题的地方通常都会立刻暴露。

方法一:人工自动执行(Linux 服务器侧)

Ubuntu 26.04 / 24.04 / 22.04 / 20.04 / 18.04、Debian、任意 Linux 发行版上都通用。以 root 执行:

#!/usr/bin/env bash
# enable_bbr_ubuntu.sh — 幂等,可重复运行
set -euo pipefail

# 1. 检查内核版本
KV=$(uname -r | awk -F. '{print $1"."$2}')
MAJ=${KV%.*}; MIN=${KV##*.}
if (( MAJ < 4 || (MAJ == 4 && MIN < 9) )); then
  echo "ERROR: 内核版本 $(uname -r) 太老,需要 4.9+"
  echo "  Ubuntu: sudo apt install --install-recommends linux-generic-hwe-\$(lsb_release -sr)"
  exit 1
fi

# 2. 立刻加载 BBR 模块
modprobe tcp_bbr

# 3. 开机持久化加载
echo "tcp_bbr" > /etc/modules-load.d/bbr.conf

# 4. 写入 sysctl 持久配置(幂等:先删除旧的,再追加)
sed -i '/^net\.core\.default_qdisc/d;/^net\.ipv4\.tcp_congestion_control/d' /etc/sysctl.conf
cat >> /etc/sysctl.conf <<EOF
# --- BBR 拥塞控制(丢包链路必备) ---
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF

# 5. 立即生效
sysctl -p

# 6. 验证
echo "--- 验证结果 ---"
echo "当前算法: $(sysctl -n net.ipv4.tcp_congestion_control)"
echo "当前队列: $(sysctl -n net.core.default_qdisc)"
echo "可用算法: $(sysctl -n net.ipv4.tcp_available_congestion_control)"
lsmod | grep tcp_bbr || echo "WARN: tcp_bbr 未在 lsmod 中"

保存为 enable_bbr.shchmod +x 并以 root 运行即可。5 秒完成,全过程无害幂等——重复运行不会重复添加行。

如果对端是 Windows 11(客户端诊断脚本,PowerShell 5.1+,不改注册表,只测量):

# check-tcp-throughput.ps1 — 客户端侧带宽验证
$ErrorActionPreference = 'Stop'
$targets = @('目标域名或 IP:8317/大文件路径')  # 替换为你的目标
foreach ($t in $targets) {
    Write-Host "→ 测试 $t"
    $stopwatch = [Diagnostics.Stopwatch]::StartNew()
    try {
        $r = Invoke-WebRequest -Uri "http://$t" -TimeoutSec 90 -UseBasicParsing
        $stopwatch.Stop()
        $sec = [math]::Round($stopwatch.Elapsed.TotalSeconds, 2)
        $kb  = [math]::Round($r.RawContentLength / 1024, 0)
        $sp  = if ($sec -gt 0) { [math]::Round($kb / $sec, 1) } else { 0 }
        Write-Host ("  OK: {0} KB in {1}s = {2} KB/s" -f $kb, $sec, $sp) -ForegroundColor Green
    } catch {
        Write-Host "  失败: $_" -ForegroundColor Red
    }
}

Windows 11 客户端不需要改任何设置——它默认的 TCP 拥塞控制(CTCP)在客户端角色下表现足够好;真正的瓶颈永远在服务器侧发送 TCP 数据时

如果对端是 macOS 26(客户端诊断脚本,用系统自带 curl):

#!/usr/bin/env bash
# check-tcp-throughput-macos.sh — 客户端带宽验证
set -euo pipefail
TARGETS=(
  "目标域名或 IP:8317/大文件路径"
)
for t in "${TARGETS[@]}"; do
  echo "→ 测试 $t"
  curl -s -o /dev/null \
    -w "  HTTP: %{http_code}  time: %{time_total}s  speed: %{speed_download} B/s\n" \
    --max-time 90 "http://$t"
done

macOS 26 本身用的是 BSD 内核,它有自己的拥塞控制(默认 cubic-like),但同样:客户端侧几乎不影响国际链路吞吐

方法二:AI Agent 自动配置(推荐让 Agent 一次性搞定所有 VPS)

如果你手上有多台 VPS 需要一起改,交给 Claude Code / Codex / Gemini CLI 这类支持 Bash 工具的 AI Agent 是最省事的做法。下面这个 prompt 我实测过——给一个 Agent 加上你所有 VPS 的 SSH 访问权限,然后直接扔过去

【任务】在多台远程 Linux 主机上启用 TCP BBR 拥塞控制 + fq qdisc,实测并出报告。

【要求】
1. 依次 SSH 到以下主机(我提供 SSH 别名,你不需要知道真实 IP):
   host-a, host-b, host-c
2. 对每台:
   - 检查内核版本,如果 < 4.9 请立刻停止并向我报告
   - 幂等地开启 BBR:modprobe + /etc/modules-load.d/bbr.conf + /etc/sysctl.conf 追加两行 (default_qdisc=fq, tcp_congestion_control=bbr)
   - sysctl -p 生效
   - 验证 sysctl -n net.ipv4.tcp_congestion_control 输出为 bbr
3. 在改动前和改动后,都跑一遍从我本地 curl 大文件的测试(你决定用哪个 URL 作为 baseline,选一个 1 MB+ 的静态文件),记录 speed_download 数值。
4. 最后给我一个 Markdown 表格,列 hostname / before speed / after speed / 提升倍数。

【安全边界】
- 修改前先备份 /etc/sysctl.conf
- 每一步失败立即中止,不要试图"猜"或"绕过"
- 每台机器改动前请打印它当前的 kernel version 和 congestion control 让我确认
- 全程不要动 /etc/sysctl.conf 里其它已有的行

Agent 会自己规划步骤、执行、比对结果、汇总输出。整个过程你只需要给它 SSH 配置权限就行,不需要在每台机器上手动登陆。

真实截图:开启后立刻跑同样的测试,速率从 30 KB/s 冲到 500-1000 KB/s

图 6:这是真的。同一台机器,同一个测试脚本,同一时刻,仅仅"改了一行 sysctl",吞吐涨了 15-24 倍。图中三个数字分别对应三台 VPS。

验证:确认配置真的生效

改完之后有三样东西必须都对上:

  1. sysctl -n net.ipv4.tcp_congestion_control → 输出 bbr
  2. sysctl -n net.core.default_qdisc → 输出 fq
  3. lsmod | grep tcp_bbr → 有一行(模块已加载)

任何一样对不上,说明脚本没跑全或者被别的配置文件覆盖了(比如 /etc/sysctl.d/*.conf 里有优先级更高的旧值)。

真实截图:验证配置在重启后也依然生效

图 7:三个文件、三个 sysctl、一个 lsmod 全部对上,就是"永久生效"了。也可以用 ss -tinp | grep bbr 观察实际的 TCP 连接是不是在用 BBR。


六、Q&A

Q1. BBR 到底是 Google 在生产上用的吗?

是的。Google 从 2016 年在 YouTube 上大规模启用 BBR。根据 ACM Queue 论文披露的数据,YouTube 的平均吞吐提升了 4%,某些国家(尤其是链路质量差的地区)提升可达 14%;中位数视频延迟减少 33%。Google Cloud、YouTube 的所有服务器都默认跑 BBR。

Q2. BBR v1 / v2 / v3 有什么区别?我要不要用最新的?

Q3. 为什么 fq qdisc 是必须的?

BBR 的核心机制之一是 pacing(节奏化发送)——它不像 CUBIC 那样把一批包"啪"一下推出去,而是均匀地分散在一个时间段里发。这就需要底层 qdisc(队列管理算法)能够按精确时间点释放包。fq(Fair Queue)就是干这个的。用 pfifo_fast(老默认值)跑 BBR 会失效很多优化。

Q4. 会不会跟 KCP、Hysteria2、WireGuard 等 UDP 方案冲突?

不会。BBR 只作用于 Linux 内核里的 TCP 栈。你的 UDP 隧道协议(Hysteria2、WireGuard、Easytier)在应用层跑自己的拥塞控制,跟 BBR 完全独立。但是——如果你的 UDP 隧道里承载了 TCP 流量(比如你 SSH、HTTP、rsync 走隧道),那么隧道两端的机器都开 BBR,隧道内的 TCP 依然会享受 BBR 的加速。这是最常见也最实用的场景。

Q5. 我改了但没效果,怎么排查?

按顺序检查:

  1. sysctl -n net.ipv4.tcp_congestion_control 是不是 bbr?如果不是,说明配置没生效(可能被 /etc/sysctl.d/ 里其他文件覆盖)。
  2. 内核太老?uname -r 看下,4.9 以下不行。
  3. 你测的是不是 UDP 流量?BBR 对 UDP 完全无效。
  4. 底层链路是不是本来就没瓶颈?如果 CUBIC 也能跑满,BBR 只会持平不会更快。BBR 只解决"因为丢包被 CUBIC 冤枉减速"的场景。
  5. 客户端侧(比如你从家里的 Windows 上访问)也可能是瓶颈——但通常客户端下载数据时,是服务器侧发送,所以只需要改服务器;只有当你上传大文件到服务器时,客户端的 TCP 发送方拥塞算法才会有影响。

Q6. 除了 BBR 还有别的手段吗?比如 CDN、CloudFlare?

有,但都不如 BBR 简单:

大多数场景下,先启用 BBR,评估之后如果吞吐依然不满意(一般是丢包 > 20% 或有严重时段拥塞),再考虑 UDP 隧道加中转。

Q7. 这个改动会不会影响我 VPS 上其他服务?比如 Nginx 反代?

不会。BBR 是一个全局默认——所有新的 TCP 连接都会用它,包括 Nginx 上游连接、你的应用与数据库、rsync、apt 更新,等等。老连接维持原来的算法直到关闭。这个改动没有任何服务需要重启。你不需要重新加载 nginx,也不需要重启数据库。


七、结语

Linux 网络子系统里"改一行就有巨大收益"的地方其实不多,BBR 是其中最典型的一个。它的门槛低(脚本 3-5 行)、副作用可控(几乎没有)、收益直观(吞吐 15-24 倍)、场景明确(丢包 5-20% 的国际链路)。

如果你手上有任何跨境 Linux 服务器,无论是自用 VPS 还是给团队搭的开发环境,我建议现在就跑一遍前面的脚本,5 分钟的事,效果立竿见影。

网络优化没有银弹,但 BBR 是我这些年见过最接近银弹的一个。


参考资料