上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器的 Linux 环境中启用 BBR 拥塞控制,降低网络游戏跨国玩家延迟?

发布人:Minchunlin 发布时间:2025-09-04 08:48 阅读量:1205


凌晨 2 点,我还在调试湾仔机房里那排崭新的 2U 服务器。空调出风口对着我呼呼作响,脚边是刚拆封的光模块包装袋。值班群里,美国西海岸和菲律宾玩家吐槽“夜里团战一到就卡、技能延迟飙到 200+ms”。Grafana 上的两条线像心跳一样波动:RTT p95 从 140ms 抬到了 220ms,重传率 在高峰期窜到 4% 以上。带宽其实还没打满,我第一反应是——典型的 Bufferbloat(队列膨胀)。
我在跳线架前蹲了三分钟,决定从传输层下手:把默认的 CUBIC 切到 BBR,再把出口队列换成可精准节流的 fq。这篇手记,就是那一夜我如何在香港服务器上启用和落地 BBR,并把跨国玩家的体感延迟从“能玩但难受”拉回“能打团能连招”。

我的现场环境(供你对照)

不同硬件/内核/网络商会影响效果;你可以把下面当作一个“可复制的标尺”。

机房/网络

  • 位置:香港(HKG)
  • 运营商:双上联(IX + 国际专线),提供 BGP 接入
  • 上行:10GbE(骨干) + 1GbE(测试/备用)

服务器硬件

参数
机型 2U 独立服务器
CPU AMD EPYC 7443P(24C/48T,基频 2.85GHz)
内存 128GB DDR4-3200
系统盘 2 × 1.92TB NVMe(RAID1,mdadm)
网卡 Intel X710-DA2(双口 10GbE,支持 RSS/多队列)

操作系统

  • CentOS 7.9(默认内核 3.10) → 通过 ELRepo 升级到 kernel-ml(≥ 5.x/6.x)
  • 部分边缘代理/房间服跑在 Ubuntu 20.04 LTS(5.4 内核)

业务侧

  • 游戏房间服(TCP:匹配/登录/聊天/WebSocket;UDP:状态同步/帧数据)
  • 前置接入:Envoy + Nginx(TLS 终止 & 反向代理)

为什么是 BBR?一句话原理 + 三张图的认知

  • BBR(Bottleneck Bandwidth and RTT)不是靠丢包来判断拥塞(和 Reno/CUBIC 不同),而是估算链路瓶颈带宽与传播时延,用**精确节流(pacing)**把发送速率卡在“既不饿网、也不挤队列”的水平。
  • 当晚我们的问题不是“带宽不够”,而是突发业务堆在出口队列,让玩家的排队时延变成了主要矛盾。BBR + fq 能直接命中这个问题。
  • BBR 只对 TCP 流直接生效;但把根 qdisc 换成 fq 后,UDP 也能受益于更合理的队列调度(突发被平滑,排队更短)。

经验提示:开启 BBR 后,“吞吐更稳 + p95/p99 延迟更低”一般是可以预期的;但极限吞吐在某些链路上未必高于 CUBIC。我们的目标是战斗场景体感而非极限测速成绩。

升级/启用 BBR 的两条路径

  • 路径 A(CentOS 7):先升级到新内核(ELRepo 的 kernel-ml),再切换 qdisc 与 TCP 拥塞算法。
  • 路径 B(Ubuntu 20.04+/Debian 10+):原生内核即可启用,大多不需要换核。

下面我把 CentOS 7 的“完整落地”写成可复制的 Runbook;Ubuntu 也附上简表。

路径 A:CentOS 7 一次到位的实操 Runbook

目标:从 3.10 内核切到 ≥5.x/6.x(自带 BBR),将根队列设为 fq,拥塞控制设为 bbr,并完成网卡/中断/容器等周边收尾。

风险共识与回滚预案(真的很重要)

维护窗口:至少 30 分钟,可回滚(保留旧内核)

回滚方法:GRUB 选择旧内核启动;或 grub2-set-default 切回旧条目

外围检查:

第三方内核模块(DPI/加密卡/存储 HBA)兼容性

监控/采集 Agent(eBPF 版本、内核头文件)

1)确认当前内核与可用拥塞算法

uname -r
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.core.default_qdisc

预期:3.10 环境看不到 bbr,default_qdisc 多为 pfifo_fast。

2)启用 ELRepo & 安装 kernel-ml

# 导入 key 与源
sudo rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
sudo yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm

# 安装新内核(长期稳定/主线之一)
sudo yum --enablerepo=elrepo-kernel install -y kernel-ml

# 查看可用的内核条目
sudo awk -F\' '/menuentry / {print $2}' /etc/grub2.cfg

注:生产上我倾向于 kernel-ml;如果你更保守,也可以选 kernel-lt(长期支持)。

3)设置默认启动条目(BIOS/UEFI 两种路径)

# BIOS 机器
sudo grub2-set-default 0
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

# UEFI 机器
sudo grub2-set-default 0
sudo grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

重启:

sudo reboot

4)重启后验证内核与 BBR 可用性

uname -r
sysctl net.ipv4.tcp_available_congestion_control | cat

预期:输出包含 bbr cubic reno 之类。

5)切换根队列为 fq,拥塞算法为 bbr(永久生效)

编辑 /etc/sysctl.d/99-bbr.conf:

net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
# 推荐的 socket 缓冲区与 backlog 调优(按需)
net.core.rmem_max=268435456
net.core.wmem_max=268435456
net.ipv4.tcp_rmem=4096 87380 268435456
net.ipv4.tcp_wmem=4096 65536 268435456
net.core.somaxconn=4096
net.ipv4.tcp_fastopen=3

加载并验证:

sudo sysctl --system
sysctl net.ipv4.tcp_congestion_control
sysctl net.core.default_qdisc

6)确认 qdisc 生效(对物理网卡/聚合口分别确认)

# 以 eth0 举例
sudo tc -s qdisc show dev eth0 | sed -n '1,10p'

预期:根队列为 fq,limit/flow_limit/quantum 等参数可见。

7)多队列网卡与中断亲和(X710/RSS)

高并发房间服下,软中断负载可能成为隐藏瓶颈。把队列拉开、避免单核热区,可进一步降低抖动。

# 查看中断分布
cat /proc/interrupts | egrep -i 'ixgbe|i40e|ens|eth'

# 临时示例:将 eth0 的 RPS 打到 0-7(8核示例)
echo f0 > /sys/class/net/eth0/queues/rx-0/rps_cpus

echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
for i in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do echo 4096 > $i; done

# 生产建议:结合 irqbalance 或手工按 NUMA 绑核

8)容器/命名空间的 qdisc 覆盖

Docker/Podman 下,容器侧使用 veth,root qdisc 需要在宿主机的 veth 对端设置:

# 找到 veth 对端(示意)
ip link
sudo tc qdisc replace dev vethXXXX root fq

我在那晚踩的一个坑:宿主机 eth0 是 fq,容器内还在用 pfifo_fast,导致部分流量仍然排队膨胀。把 veth 对端也换成 fq 后,UDP 抖动肉眼可见地收敛。

9)NAT/防火墙与连接跟踪

并发上来后,conntrack 槽位不足会放大重试与排队:

sudo sysctl -w net.netfilter.nf_conntrack_max=2621440
sudo sysctl -w net.netfilter.nf_conntrack_buckets=655360

路径 B:Ubuntu/Debian 简表

# 启用 BBR(5.4+ 通常原生可用)
sudo bash -c 'cat >/etc/sysctl.d/99-bbr.conf' <<'CONF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
CONF

sudo sysctl --system
sysctl net.ipv4.tcp_congestion_control
sudo tc -s qdisc show dev eth0 | head

观测与验证:不是“开了就算”,要看“怎么跑”

1)看 TCP 连接是否真的在用 BBR

# 选择一个活跃 TCP 连接 PID
ess -tna | grep ESTAB | head
# 查看拥塞算法、拥塞窗口、pacing 等
sudo ss -ti dst :443 | sed -n '1,30p'

预期:能看到 cong bbr、pacing_rate、delivery_rate 等字段。

2)合成压测(跨地域)

我用三台探针(东京、新加坡、洛杉矶)跑 iperf3 和 mtr,压测 TCP/UDP 的延迟与抖动:

iperf3 -c <HKG_IP> -t 60 -P 4
iperf3 -u -c <HKG_IP> -b 50M -t 60
mtr -rwzbc100 <HKG_IP>

3)前后对比数据(节选)

业务尖峰(UTC+8 晚上 22:00-23:00)窗口的抓取。单位:毫秒/百分比。

地区 指标 调整前(CUBIC+pfifo_fast) 调整后(BBR+fq) 变更
US-West(洛杉矶) RTT p50 145 132 -8.9%
  RTT p95 221 176 -20.4%
  RTT p99 268 209 -22.0%
  TCP 重传率 3.8% 1.2% -2.6pp
  UDP 抖动(ms) 12.1 8.4 -30.6%
菲律宾(马尼拉) RTT p50 68 61 -10.3%
  RTT p95 112 87 -22.3%
  RTT p99 149 115 -22.8%
  TCP 重传率 2.1% 0.9% -1.2pp
  UDP 抖动(ms) 7.8 5.5 -29.5%

解释:UDP 的抖动下降,主要来自 fq 根队列 的突发平滑与更公平的排队,而非 BBR 本身(BBR 作用于 TCP)。

业务侧联动:别让应用层“打回原形”

WebSocket/长连接:启用 TCP_NODELAY,避免 Nagle 延迟;遵循内核 pacing,不做用户态延迟发送。

Envoy/Nginx:

关闭过度的 proxy_buffering,对实时通道使用更小的 proxy_buffers。

开启 reuseport,把 accept 压力均摊到多核。

SO_MAX_PACING_RATE:如果你有大包突发上传(如录像/回放),可以按流量类型为套接字设置上限,让 pacing 更可控。

Nginx 片段示例:

worker_processes auto;
worker_rlimit_nofile 200000;

events { use epoll; multi_accept on; }

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    # 按需调小实时接口的缓冲
    proxy_buffering off;
}

stream {
    server {
        listen 7001 reuseport;
        proxy_pass backend_pool;
    }
}

常见坑位与我的处理笔记

只改了拥塞算法,忘记换根 qdiscBBR

需要精确节流;pfifo_fast 不支持 pacing。必须把 default_qdisc 换成 fq(fq_codel 也可,但我在高并发房间里更偏好 fq 的流级别公平)。

容器 veth 忘记配置

qdisc宿主生效、容器不生效,表现为 UDP 抖动改善有限。把 veth 对端也换成 fq 即可。

老内核 + Virtio 的 GSO/TSO

兼容性过旧内核的 pacing 和大包分片存在已知边角。升级到 5.x/6.x 后基本解决。不建议在 3.x 上“硬开” BBR 补丁。

队列太深

某些网关出厂 txqueuelen 夸张大,短流排队严重:

ip link set dev eth0 txqueuelen 1000 # 按需要

conntrack 槽位不够

高峰期新建连接抖动、握手重试。增大 nf_conntrack_max/buckets 并监控饱和度。

测评方法陷阱

纯 speedtest有时反映不出改动价值。要看 p95/p99 延迟、重传率、UDP 抖动,并在业务时段测。

跨境路由与对端队列

我们在一段时间内观察到某电信对端“晚高峰丢包”,BBR 无法治愈一切——该打的路由/专线还是要打,必要时在 HKG 前加 边缘 PoP/Relay。

高阶补充:qdisc 的取舍与参数小抄

fq:支持 pacing、流级别调度,默认即可;可以按需调 flow_limit(默认 100)以容纳更多并发流。

fq_codel:同样支持 pacing,并自带主动队列管理(AQM),在复杂混部场景有优势。

# 切换为 fq_codel(如果你偏好 AQM)
sudo tc qdisc replace dev eth0 root fq_codel
sudo tc -s qdisc show dev eth0 | head

复盘与结尾:凌晨 3:40 的那条弹幕

重启后一刻,监控的重传率曲线先掉了一截。接着 tc 的统计里,drops 不再连续增长。我们把 mtr 又跑了一轮,p95 从 220ms 掉回 170ms 左右。三点半,西海岸那边还在打架,公屏上飘过一句:“今晚不卡,能连上三段了。”

我靠在机柜门上,听着风机声,脑子飞快过一遍刚才的改动清单——这不是“换个算法”的玄学,而是一套面向排队时延的工程化组合拳:新内核 + BBR + fq + 中断与队列治理 + 业务侧配合。至于你在自己的环境如何拿捏节奏?

建议先做一台影子服试点:两周;再在低峰灰度;最后在业务高峰前完成全量切换——用数据说话。

附:一键化脚本(CentOS 7)

仅供参考,请在测试机验证后再上生产。

#!/usr/bin/env bash
set -euo pipefail

# 1) 安装 ELRepo & kernel-ml
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-ml

# 2) 设默认内核
if [ -d /boot/efi/EFI/centos ]; then
  grub2-set-default 0
  grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
else
  grub2-set-default 0
  grub2-mkconfig -o /boot/grub2/grub.cfg
fi

echo "net.core.default_qdisc=fq" > /etc/sysctl.d/99-bbr.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.d/99-bbr.conf
cat <<EOF >> /etc/sysctl.d/99-bbr.conf
net.core.rmem_max=268435456
net.core.wmem_max=268435456
net.ipv4.tcp_rmem=4096 87380 268435456
net.ipv4.tcp_wmem=4096 65536 268435456
net.core.somaxconn=4096
net.ipv4.tcp_fastopen=3
EOF

sysctl --system

# 3) 可选:提高 conntrack
sysctl -w net.netfilter.nf_conntrack_max=2621440
sysctl -w net.netfilter.nf_conntrack_buckets=655360

# 4) 提示
echo "[OK] Kernel 安装完成,请执行 reboot 后验证:uname -r && sysctl net.ipv4.tcp_congestion_control"

如果你此刻也站在香港机房里,手里捏着扳手、盯着那条 p99 曲线,那么我建议你:先把排队问题打穿。BBR 不是银弹,但在我们这类“跨国玩家、晚高峰突发”的游戏场景里,它是最务实的一记重拳。祝你今晚也能早点收工。

目录结构
全文