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

香港服务器上,我们如何设置并结合 CN2 带宽来优化游戏 TCP/UDP 传输,降低玩家跨境延迟?

发布人:Minchunlin 发布时间:2025-09-09 09:11 阅读量:1046


那天是周五的凌晨两点,香港旺角机房的空调像往常一样嗡嗡作响。我盯着大屏上从华南到香港的 RTT 曲线,它在 20:00–23:30 间像心电图一样抖动。社区里玩家的反馈已经炸开了锅:“晚高峰打团抖成 PPT,技能延迟 200+ms。”
我们服务器在香港,骨干走的是普通国际回程。直觉告诉我,这不是服务器算力问题,而是跨境链路在晚高峰被挤爆。我决定把**CN2(China Telecom Next Carrier Network)**拉进来:用 CN2 GIA 兜住电信用户、用 CMI(中国移动国际)兜住移动用户;必要时再给联通用户提供 9929/精品网备胎。思路清晰后,我在机房铺开笔记本,开始下面这套“分流 + 内核 + 队列 + MTU + 监控”组合拳。

环境与目标

我们的目标

  • 晚高峰跨境延迟(华南/华东 → HK)降到 40–70 ms 的稳定区间,抖动(Jitter)< 10 ms
  • 游戏 TCP:丢包友好、避免重传雪崩
  • 游戏 UDP:抗抖动、避免分片、尽量走就近优线路
  • 故障自动切换:CN2/CMI 任一线路波动不殃及全量玩家

机器与线路(真实可落地)

角色 规格/参数
机型 1× Dell R7525(或同档)
CPU AMD EPYC 7313P(16C/32T)
内存 128 GB DDR4
磁盘 2× NVMe (PCIe 3.0,读 3GB/s)
网卡 Intel XXV710(25G,RSS/TSO/GRO 支持)
操作系统 CentOS 7.9(kernel 升级至 5.x,启用 BBR)
上游 CN2 GIA 100 Mbps commitCMI 200 Mbps commit(或等价)
形态 直连 +(必要时)GRE/WireGuard 隧道;多表路由分流
游戏 TCP(登录/交易)+ UDP(战斗/状态)混部,端口段 20000–21000

方案总览(四层优化栈)

  • 线路与分流:CN2/CMI/(9929 可选)多挂;按来源 ASN / 目的网段做策略路由,让电信→CN2、移动→CMI,避免“走错线”。
  • 链路与 MTU/MSS:如用隧道(GRE/WireGuard/IPsec),计算开销,设置 MTU 与 MSS Clamping,避免分片。
  • 主机网络栈:CentOS 7 升级内核启用 BBR + fq/fq_codel/cake;合理的 rmem/wmem/backlog/conntrack。
  • 应用层:TCP(TFO/SACK/低重传抖动)、UDP(NOTRACK + recvmsg/recvmmsg + SO_REUSEPORT)、端口隔离与限速保护。
  • 可观测与回归:iperf3/mtr/tcping/SmokePing + 黑白盒监控 + 自动故障切换脚本。

实操步骤

以下命令默认 CentOS 7.9。BBR 需要 Linux 4.9+,故我们从 ELRepo 升级到 5.x 内核。

Step 1:升级内核、启用 BBR 与队列

安装 ELRepo 并切换 kernel-ml(5.x)

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 grub2-set-default 0
sudo reboot

重启后:

uname -r  # 确认5.x内核

启用 BBR 与 FQ:

cat <<'EOF' | sudo tee /etc/sysctl.d/99-game-net.conf
# 拥塞算法
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 缓冲相关(UDP/TCP)
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# 队列/接收
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 16384
net.core.somaxconn = 65535

# 连接追踪(按需微调)
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180

# 其他
net.ipv4.tcp_mtu_probing = 1     # 避免路径MTU问题
net.ipv4.tcp_sack = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_fastopen = 3        # client+server
EOF

sudo sysctl --system
sysctl net.ipv4.tcp_congestion_control  # 看到 bbr 即生效

若内核已包含 cake(部分内核需要模块或 backport),可以把根队列切为 fq_codel/cake(见 Step 3)。

Step 2:打通 CN2/CMI 与健康探测

直连优先;若运营商提供的是隧道到 HK POP(常见 GRE/WireGuard),注意 MTU。

以 WireGuard 为例(加密少损耗,易运维):

# 安装 WireGuard(EL7)
sudo yum install -y epel-release
sudo yum install -y wireguard-dkms wireguard-tools

# /etc/wireguard/wg-cn2.conf
cat <<'EOF' | sudo tee /etc/wireguard/wg-cn2.conf
[Interface]
Address = 10.10.2.2/30
PrivateKey = <SERVER_PRIVATE_KEY>
ListenPort = 51820
MTU = 1420

[Peer]
PublicKey = <CN2_POP_PUBLIC_KEY>
Endpoint = cn2.pop.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 15
EOF

sudo systemctl enable wg-quick@wg-cn2
sudo systemctl start wg-quick@wg-cn2
ip link show wg-cn2

MTU 计算:以 WireGuard 为例,开销 ~60–80 Byte,1420–1440 常见。GRE 通常建议 1476。后文会配合 TCP MSS clamp。

健康探测与自动切换(简版):

# /usr/local/bin/healthcheck_cn2.sh
#!/usr/bin/env bash
TARGET="223.120.2.1"  # CN2/电信近端探针或对端IP
TABLE=100
IF="wg-cn2"
COUNT=3
LOSS=$(ping -I $IF -c $COUNT -W1 $TARGET | awk -F',' '/packet loss/{print $3+0}')
if (( LOSS >= 66 )); then
  # 高丢包 -> 降权(删除策略),走默认CMI
  ip rule del fwmark 0x1 table $TABLE 2>/dev/null
else
  # 健康 -> 恢复优先策略
  ip rule add fwmark 0x1 table $TABLE pref 100 2>/dev/null
fi

chmod +x /usr/local/bin/healthcheck_cn2.sh
echo '*/1 * * * * root /usr/local/bin/healthcheck_cn2.sh' | sudo tee /etc/cron.d/hc_cn2

Step 3:按运营商/ASN 分流(电信→CN2、移动→CMI)

我们采用 ipset + iptables mangle 标记 + 多表路由:

准备路由表:

echo "100 cn2" | sudo tee -a /etc/iproute2/rt_tables
echo "200 cmi" | sudo tee -a /etc/iproute2/rt_tables

# 指定下一跳(示例:wg-cn2 / eth0)
sudo ip route add default dev wg-cn2 table cn2
sudo ip route add default via 10.0.0.1 dev eth0 table cmi  # CMI 直连网关

维护电信/移动网段的 ipset(可按 ASN 过滤生成,下面示例手动演示少量前缀——生产用脚本每天拉 APNIC/各家 CIDR 刷新):

sudo ipset create ct hash:net -exist
sudo ipset add ct 36.128.0.0/10
sudo ipset add ct 61.48.0.0/12
# ...(省略:生产环境用脚本按 AS4134/AS4809 等生成)

sudo ipset create cm hash:net -exist
sudo ipset add cm 39.128.0.0/10
sudo ipset add cm 117.128.0.0/9
# ...(省略:AS9808、AS56040 等)

mangle 标记 + policy route:

# 电信 -> 打标 0x1
sudo iptables -t mangle -A PREROUTING -m set --match-set ct src -j MARK --set-mark 0x1
sudo ip rule add fwmark 0x1 table cn2 pref 100

# 移动 -> 打标 0x2
sudo iptables -t mangle -A PREROUTING -m set --match-set cm src -j MARK --set-mark 0x2
sudo ip rule add fwmark 0x2 table cmi pref 200

如果你做回程分流(根据目的地选择出口),把 src 改成 dst 即可。

联通 9929 可另建 table 300 与 ipset cu,原则同上。

Step 4:队列管理(fq_codel/cake) 降抖动

晚高峰的排队抖动比纯延迟更伤游戏体验。我们在出口施加 Smart Queue Management:

# fq_codel(内核通用)
sudo tc qdisc replace dev eth0 root fq_codel target 5ms interval 100ms limit 20000 flows 4096

# 如果内核有 cake(更智能)
# 例:总带宽 300Mbps(上下行可分别配置)
sudo tc qdisc replace dev eth0 root cake bandwidth 300M besteffort nat

落地经验:给外发方向加队列效果最明显。入方向只能在上游做(或用 IFB 中转,配置复杂度上升)。当 commit 只有 100–200 Mbps 时,cake对晚高峰控抖很有效。

Step 5:MTU/MSS 与分片火坑

有隧道就有开销。WireGuard 1420、GRE 1476 是稳妥起点。为防 TCP 分片,做 MSS Clamp:

# 针对外发 TCP SYN
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu

UDP 无 MSS 概念,避免应用层 payload 过大(<1200 Byte 是比较保险的通用值),尤其穿隧道时。

Step 6:NIC 与 CPU 亲和(低延迟不是只靠内核参数)

# 调大 ring buffer
sudo ethtool -G eth0 rx 4096 tx 4096

# 自适应中断合并(低延迟/高吞吐取平衡)
sudo ethtool -C eth0 adaptive-rx on adaptive-tx on

# 观察中断分布
cat /proc/interrupts | egrep 'eth0|ixgbe|i40e|iavf'

# 开启RPS/RFS(根据CPU核数分散软中断)
echo ffffffff | sudo tee /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 32768 | sudo tee /proc/sys/net/core/rps_sock_flow_entries
for f in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do echo 4096 | sudo tee $f; done

# SO_BUSY_POLL 可以少量开启(谨慎)
echo 50 | sudo tee /proc/sys/net/core/busy_poll
echo 50 | sudo tee /proc/sys/net/core/busy_read

irqbalance 默认已开;极端低延迟场景可手工绑核(NUMA 亲和),但要结合压测与温度监控。

Step 7:UDP 高并发与 NOTRACK(游戏房间用得上)

对指定 UDP 端口段关闭 conntrack,降低 CPU 与延迟:

# raw 表 NOTRACK
sudo iptables -t raw -A PREROUTING -p udp --dport 20000:21000 -j NOTRACK
sudo iptables -t raw -A OUTPUT     -p udp --sport 20000:21000 -j NOTRACK

应用侧建议开启 SO_REUSEPORT,多进程抢同端口,提高并发收包能力;读取用 recvmmsg() 比 recvfrom() 更省系统调用。

Step 8:TCP 的稳与快

BBR 已启;保留 SACK + TS。

TCP Fast Open (TFO):对新连接首包可提速,但国内少部分中间盒会干扰;若观测到异常握手,降级关闭或仅对可信 NAT 开启。

超时与重传:适当降低 tcp_syn_retries=5→3,避免慢死链路拖垮排队;tcp_fin_timeout=15。

Step 9:安全与限速

晚高峰常伴随UDP Flood 与 SYN Flood:

# SYN cookie
sysctl -w net.ipv4.tcp_syncookies=1

# 基础限速(示例)
sudo iptables -A INPUT -p udp --dport 20000:21000 -m hashlimit \
  --hashlimit 2000/sec --hashlimit-burst 4000 --hashlimit-mode srcip \
  --hashlimit-name udprate -j ACCEPT
sudo iptables -A INPUT -p udp --dport 20000:21000 -j DROP

真正的大流量还是建议上游清洗或引入 Anycast + 多点就近。

监控与压测方法

  1. 链路层:mtr -rwzbc100 目标IP(看丢包/波动);iperf3 -u -b 50M(UDP 抖动与丢包)。
  2. TCP 首包:tcping 对登录服端口测 RTT。
  3. 持续可视化:SmokePing 针对CN2 与 CMI 分别打点;Prometheus + Grafana 监控 conntrack、qdisc 队列、软中断、网卡丢包。

优化前后对比(实测样本)

地区 → HK 业务 优化前 RTT (avg) 优化后 RTT (avg) 抖动 (p95-p50) 备注
深圳(电信) UDP 85 ms 38 ms 5 ms CN2 分流
广州(移动) UDP 110 ms 55 ms 7 ms CMI 分流
上海(电信) TCP 140 ms 68 ms 8 ms BBR + MSS Clamp
成都(移动) UDP 160 ms 78 ms 9 ms fq_codel 生效

统计口径:晚高峰 20:00–23:00,5 分钟粒度,剔除异常峰值与丢包>10% 样本。

常见坑与我的填坑记录

只做单向优化:接入 CN2 只优化去程或回程的一侧,另一侧仍走拥塞网,RTT 抖动仍高。
对策:尽量让往返都能命中优线路(必要时两侧都引流到同一 POP)。

隧道 MTU 算错:设置 1500 不管,结果 TCP 分片、UDP 丢包。
对策:WireGuard 1420 起步,配合 --clamp-mss-to-pmtu,UDP payload 控制 < 1200。

cake + BBR 的参数冲突:过小的 quantum/flows 导致吞吐飘。
对策:以出口带宽为基准配置 cake,观察 tc -s qdisc 的丢包与延迟分布做回归。

TFO 在部分 NAT 异常:玩家登录卡死。
对策:对登录服灰度启用,异常 ASN 段降级关闭。

NOTRACK 过猛:忘了对外联机调试端口;导致排查困难。
对策:只对游戏房间端口段 NOTRACK,控制面/管理面仍走 conntrack。

ipset 更新不及时:前缀漂移后电信用户走到 CMI。
对策:每天 4 次自动拉取 APNIC/各 ASN 前缀,原子更新 ipset。

上游突发限速:commit 不够,出口包被丢。
对策:cake 的 bandwidth 与实际 commit 保持一致,预留 5–10% 头寸。

附:自动更新 ASN 前缀到 ipset(示例骨架)

生产环境可从 APNIC/各运营商公开表或自有路由器导出。这里放个简化脚本骨架,思路是生成新集合 → 原子切换。

#!/usr/bin/env bash
# /usr/local/bin/update_asn_ipset.sh
set -euo pipefail

TMPSET=ct_new
REALSET=ct
ASN_LIST=("4134" "4809")  # 电信常见 ASN(示意)

ipset create $TMPSET hash:net -exist

for ASN in "${ASN_LIST[@]}"; do
  curl -fsSL "https://example.com/asn/${ASN}.cidr" | \
  awk '/^[0-9]+\./{print $1}' | while read -r cidr; do
    ipset add $TMPSET $cidr -exist
  done
done

# 原子切换
ipset swap $TMPSET $REALSET
ipset destroy $TMPSET

定时:

echo '*/15 * * * * root /usr/local/bin/update_asn_ipset.sh' | sudo tee /etc/cron.d/ipset_update

关键参数速查表

类别 参数/命令 建议
拥塞 net.ipv4.tcp_congestion_control bbr
队列 tc qdisc ... fq_codel / cake 出口设备启用,带宽按 commit 配置
缓冲 net.core.rmem_max/wmem_max 256 MiB 级别
backlog net.core.netdev_max_backlog 250k 起
追踪 nf_conntrack_* 对 UDP 房间端口 NOTRACK
MTU ip link set dev ... mtu WireGuard ≈ 1420;GRE ≈ 1476
MSS TCPMSS --clamp-mss-to-pmtu 必开
亲和 rps_cpus/rps_flow_cnt 按核数分配
ring ethtool -G rx/tx = 4096
合并 ethtool -C ... adaptive-* on on

结尾:告别“晚高峰综合征”的那个夜晚

凌晨四点,SmokePing 上两条曲线终于贴着地面走。第二天晚上八点,我把监控投到会议室的大屏,团队一起盯着那些曾经暴躁的抖动点变得安静。社区里最先冒出来的一条反馈是:“今晚团战稳得离谱,手感回来了!”

这套方案并不神秘:把用户分到该走的路上(CN2/CMI/9929),把主机栈打磨到位(BBR + 队列 + MTU/MSS),再用可观测与自动化把波动压回去。它像我们在机房里反复拧紧的一颗颗螺丝,没有花哨,但每一颗都咬得更紧。
如果你也在香港跑游戏业务、也在为跨境延迟头大,不妨按这套步骤试一遍。也许,下一次凌晨两点,你能比我更早把那条心电图拉直。

说明:本文以 CentOS 7.9 为基础演示,如你的内核仍是 3.10,请务必先按文中步骤升级到 4.9+ 才能启用 BBR。若你的上游已提供原生 CN2/CMI 直连且支持 BGP,可把 ipset+PBR 替换为 BGP 社区策略 做更精细的流量工程;但对租用型单机/单段,文中方案更容易直接落地。

目录结构
全文