部署在香港服务器上的电竞平台,如何用“线路打法”救回日韩玩家的掉线率
技术教程 2025-09-20 08:53 192


22:17(JST),东京与首尔区的排位匹配掉线率陡升到 4.8%,Discord 舆情炸开,玩家疯狂@我们。Grafana 的红线像心电图一样拉高,我坐在机房过道,背后是呼呼作响的风墙,屏幕上 MTR 里 NTT 香港—东京的某跳丢包 6%……那天我把“香港线路打法”从纸面落成了现场 SOP。这篇,就是我给后来者的全流程实操教程与避坑清单。

目标与思路

  • 目标:把部署在香港的电竞匹配与房间中继节点,针对 日本/韩国 玩家做到——
  • RTT 中位 ≤ 35ms(JP),≤ 45ms(KR)
  • 抖动 p95 ≤ 5ms
  • 跨局掉线率(UDP 会话中断)≤ 0.8%

核心思路:发挥香港的网络枢纽优势(HKIX 就地互联 + 多家国际转接商),用 多宿主 BGP + 线路定制化路由偏好 + 业务层延迟感知调度 + 内核/队列调优 + 有损环境下的 UDP 生存包,把“看起来就近”的路,真正变成 稳定低抖动 的路。

拓扑与硬件清单(真实可落地)

物理与网络

角色 规格与选型 要点
机房 香港(MEGA-i/同级别)、双路 UPS A+B、每柜 6–8kW HKIX 距离近,运营商选择多
边界路由器 2× x86 服务器(FRR BGP),AMD EPYC 7443P,128GB RAM,2×NVMe,2×25GbE(Mellanox CX4/5) 软件路由更灵活:BGP 社区、路由映射、BFD
匹配/网关节点 6× Dell R6525 同规格,256GB RAM,2×3.84TB NVMe(RAID1),双 25GbE UDP 为主的会话型业务,NIC 中断亲和很关键
接入交换机 25G/100G 叶脊(任一主流厂商),ECMP 建议脊-叶均走 L3,方便 ECMP 与快速收敛
上联/线路 Transit A(偏日本优)、Transit B(偏韩国优),本地 HKIX 若能对等更佳 “一日两家”是下限;最好再拉条备份(低承诺带宽)
操作系统 CentOS 7(建议装 ELRepo kernel-ml ≥ 5.10) 为了 BBR、fq_codel、XDP 更好支持
防火墙 机内 nftables(kernel-ml)/ iptables(原生 3.10) CentOS 7 原生 3.10 不带 nft,需要升级内核

说明:CentOS 7 自带 3.10 内核对 TCP BBR、fq_codel、XDP 支持一般,强烈建议加装 ELRepo 的 kernel-ml(保留老内核做 GRUB 回滚)。

香港线路打法:原则先行

  • “近”不等于“稳”:JP/KR 很多 eyeball ASN(2497 IIJ、2516 KDDI、17676 SoftBank、4766 KT、9318 SKB、17858 LGU+)经不同转接商路径质量差异极大。
  • 多宿主是底座:至少两家国际转接商(Transit),一条偏 JP,一条偏 KR。
  • 路由入站调优 > 出站:你看到的 traceroute 是你出站的路,会话稳定性更怕 回程 被带去新加坡或美国;要用 BGP 社区、本地优先级(local-pref) 与 上游策略沟通 把回程“拉直”。
  • HKIX 的价值:若能在 HKIX 拿到对等或直连,能显著降低抖动;至少要监控“是否绕行至非预期节点”。
  • 应用层配合:匹配服/中继服要 延迟感知,遇到路径劣化快速切换/重建房间,不要死扛。

Step 1:基线测量(必须“先测后打”)

工具:mtr, paris-traceroute, iperf3, smokeping,以及你自建的 PingMesh(全球多点小探针,建议东京/大阪/札幌、首尔/釜山至少 5 个点)。

命令样例:

# 连续 300 秒,mtr 至东京三家大眼球 AS 的任一探针
mtr -rwzbc 300 probe.tokyo.iij.example
mtr -rwzbc 300 probe.tokyo.kddi.example
mtr -rwzbc 300 probe.tokyo.softbank.example

# 到韩国三家
mtr -rwzbc 300 probe.seoul.kt.example
mtr -rwzbc 300 probe.seoul.skb.example
mtr -rwzbc 300 probe.seoul.lguplus.example

# UDP 端到端丢包/抖动观测(注意 iperf3 -u)
iperf3 -u -c probe.tokyo.iij.example -b 50M -t 120 -i 1

基线(事故夜当时的数据节选):

目的地 路径(概要) RTT 中位 抖动 p95 丢包
Tokyo (IIJ) HK → NTT HK → NTT JP → IIJ 32ms 4.3ms 0.6%
Tokyo (KDDI) HK → Transit B → SG → JP 47ms 9.1ms 2.1%
Seoul (KT) HK → Transit B → KT 39ms 3.8ms 0.5%
Seoul (SKB) HK → Transit A → CN → KR 58ms 7.6ms 1.4%

结论:东京要走 Transit A(偏日系),首尔 优先 Transit B;此外发现 SKB 回程绕路(走 CN),需要与 Transit A 用社区和优先级“谈谈”。

Step 2:内核与网络栈调优(CentOS 7 版)

2.1 升级内核(推荐)

# 安装 ELRepo 并上 kernel-ml
yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm -y
yum --enablerepo=elrepo-kernel install kernel-ml -y
# 设为默认启动项
grub2-set-default 0
grub2-mkconfig -o /boot/grub2/grub.cfg

2.2 sysctl(UDP 会话、缓冲与 BBR/FQ)

/etc/sysctl.d/99-game-tuning.conf:

# 缓冲区与队列
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 8192

# UDP 内存与最小缓冲
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384

# UDP 会话在 NAT/防火墙下的保持(视网关角色开启)
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180

# TCP 只用于登录/下载:BBR + FQ(需 kernel >= 4.9)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# MTU 与 ICMP
net.ipv4.ip_no_pmtu_disc = 0
net.ipv4.icmp_echo_ignore_all = 0
net.ipv4.tcp_mtu_probing = 1

# 反射与路由缓存
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2

sysctl --system

2.3 NIC 中断亲和与多队列

# 开足硬件队列
ethtool -l eth0
ethtool -L eth0 combined 16

# RPS/RFS:每核分发队列(按你的 CPU 拓扑写)
echo ffff > /sys/class/net/eth0/queues/rx-0/rps_cpus
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
for q in /sys/class/net/eth0/queues/rx-*; do echo 4096 > $q/rps_flow_cnt; done

# 绑核(示意,将队列 0..15 绑到 CPU 0..15)
for i in {0..15}; do
  printf "%x\n" $((1<<i)) > /proc/irq/$(grep -m1 eth0-TxRx-$i /proc/interrupts | cut -d: -f1)/smp_affinity
done

Step 3:BGP 多宿主与路由偏好(用 FRR 落地)

目标:

  • 对 JP 相关 AS(2497/2516/17676 等) 的前缀,经 Transit A 学到的路设置 更高 local-pref;
  • 对 KR 相关 AS(4766/9318/17858 等) 的前缀,经 Transit B 学到的路设置 更高 local-pref;
  • 出口向上游标注 合适的 BGP 社区(示例值仅为占位,请以你的转接商文档为准),限制不必要的跨洲再发布,争取回程就近;
  • 打开 BFD 做敏捷切换。

/etc/frr/frr.conf 样例(重点片段):

frr version 10.0
frr defaults traditional
hostname hk-edge-01
service integrated-vtysh-config
!
router bgp 65001
 bgp router-id 203.0.113.10
 no bgp default ipv4-unicast

 neighbor TRANSITA peer-group
 neighbor TRANSITA remote-as 65010        ! 你的 Transit A AS
 neighbor 198.51.100.1 peer-group TRANSITA

 neighbor TRANSITB peer-group
 neighbor TRANSITB remote-as 65020        ! 你的 Transit B AS
 neighbor 203.0.113.1   peer-group TRANSITB

 timers bgp 3 9
 !
 address-family ipv4 unicast
  neighbor TRANSITA activate
  neighbor TRANSITB activate
  neighbor TRANSITA send-community both
  neighbor TRANSITB send-community both

  maximum-paths 2
  !
  neighbor TRANSITA route-map PREF-JP in
  neighbor TRANSITB route-map PREF-KR in

  neighbor TRANSITA route-map OUT-COMMUNITY out
  neighbor TRANSITB route-map OUT-COMMUNITY out
 exit-address-family
!
# AS-Path 匹配(示意:日本与韩国常见 eyeball ASN)
ip as-path access-list JP-ASN permit _((2497)|(2516)|(17676))_
ip as-path access-list KR-ASN permit _((4766)|(9318)|(17858))_

route-map PREF-JP permit 10
 match as-path JP-ASN
 set local-preference 200
route-map PREF-JP permit 20
 set local-preference 150

route-map PREF-KR permit 10
 match as-path KR-ASN
 set local-preference 200
route-map PREF-KR permit 20
 set local-preference 150

# 对外标注社区(示例:请用你的转接商文档实际替换)
route-map OUT-COMMUNITY permit 10
 set community 65010:420 additive    ! e.g. “prefer Asia region” 占位
 set large-community 65010:100:10    ! e.g. “don’t announce to peers X” 占位
!
bfd
 profile fast
  detect-multiplier 5
  receive-interval 100
  transmit-interval 100
 !
!

关键点:

  • local-pref 只影响你端的出站选路,但回程要靠 上游社区策略 与 沟通。社区值请以运营商文档为准。
  • 对 HKIX 的对等邻居(若有),另建 peer-group,通常 local-pref 更高,同时避免将小前缀乱向 Transit 外溢。

Step 4:队列与拥塞(把抖动打下去)

我们在边界与游戏节点 egress 口启用 fq_codel(内核 ≥ 4.9 效果更好):

tc qdisc replace dev eth0 root fq_codel flows 40960 target 5ms interval 100ms ecn
tc -s qdisc show dev eth0

fq_codel 对 突发队列 与 Bufferbloat 的抑制非常有效,比赛开局瞬时“洪峰”时段尤为明显。

Step 5:会话稳定(UDP Keepalive 与 NAT/状态跟踪)

服务器侧(网关/中继):以 30s 心跳(可按房间 tick)发 极小 UDP 生存包;客户端掉 ACK 时 2 次重试后重建通道(同房间不换人)。

NAT/Conntrack:见前述 nf_conntrack_udp_timeout_stream=180;若你的边界也做 SNAT,务必把 会话端口 pin 住(hash based),避免重映射造成“伪掉线”。

防火墙(示例 nftables,kernel-ml 有 nft):

table inet game {
  set game_ports { type inet_service; elements = { 20000-20999 } }

  chain input {
    type filter hook input priority 0; policy drop;
    ct state established,related accept
    iif "lo" accept
    udp dport @game_ports accept
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept
  }
}

如果还在 3.10 内核,用 iptables:

iptables -A INPUT -p udp --dport 20000:20999 -j ACCEPT

Step 6:业务层“延迟感知”匹配(核心逻辑)

做法:匹配服每 10 秒拉一次各线路 实时健康度(RTT p50/p95、抖动、丢包),在给 JP/KR 玩家分配房间时,优先投递到得分最低的线路/中继,在房间生命周期内 监控 3 连续窗口劣化 触发“无损迁移”(server-authoritative,state 复制 + 新通道并行 3s)。

得分函数示例(伪代码):

score = 0.5 * rtt_p50 + 0.3 * jitter_p95 + 100 * loss_rate
# 若回程可观测(对端探针),额外减分项
if path_is_symmetric: 
    score *= 0.9

房间无损迁移步骤:

  • 在新线路的中继预热房间(复制状态、玩家令牌有效期 5s)。
  • 服务端同时对客户端下发“双播”窗口(老/新各发 1–2 个 tick)。
  • 检测新通道确认(收到 3 个连续 tick),切断老通道。
  • 统计上报一次“线路切换成功/失败”。

Step 7:备用“直通”与快速避险(GRE/IPIP + Anycast/BFD)

当某 Transit 某区域抖动飙升,但你在 东京/首尔 有轻量 PoP(或第三方边缘),可起备用 GRE/IPIP 到该 PoP,用其本地 Transit 出口回家。香港边界与 PoP 双向跑 BFD,当主路健康分 < 阈值时,把 JP/KR 目的前缀 临时走隧道。

GRE 建隧(示例):

ip tunnel add jpgre mode gre remote 203.0.113.200 local 203.0.113.10 ttl 255
ip link set jpgre up
ip addr add 10.254.0.1/30 dev jpgre
# 路由权重调低,平时不走
ip route add 0.0.0.0/0 dev jpgre metric 500

Step 8:监控大盘与告警门槛(别等玩家先告诉你)

  • 网络:BGP 会话(FRR Exporter)、路由收敛时延、各 AS 目的地 RTT/抖动/丢包分布(Prometheus + Grafana + Smokeping)。
  • 业务:房间建立耗时、首包时延、无损迁移成功率、局中掉线率。

告警:

  • 任一 JP/KR Eyeball 目的 RTT p50 > 45ms 或 抖动 p95 > 8ms 连续 3 分钟。
  • 房间无损迁移失败率 > 1.5%(10 分钟窗口)。
  • BGP 邻居 flap > 3 次/5 分钟或 BFD down>30s。

部署“卡点”与现场解法(真·避坑)

  • 回程不直:只看你出站 traceroute 没用,必须有 对端探针。和转接商谈社区策略(例如“仅在亚洲传播”“不向特定对等发布”),必要时 换对端 AS。
  • PMTUD 黑洞:某些上游丢 ICMP Frag Needed。解决:端口分 MTU(游戏 UDP 走 1400 或更低)、开启 tcp_mtu_probing=1。
  • ECMP + UDP 黏性:多路径下 UDP 流可能“跳路径”。在边界固定 5-tuple hash(大多默认如此),业务层无损迁移兜底。
  • irqbalance 抢核:irqbalance 把网卡中断迁走,导致延迟飙。固定 smp_affinity 后,记得把 irqbalance 屏蔽该 IRQ。
  • 容器防火墙双栈:宿主 iptables 与容器 CNI 叠加导致 conntrack 异常,排查要看 conntrack -L 与 DNAT 规则顺序。
  • fq_codel 被别的 qdisc 顶掉:有的驱动默认启了 mq + pfifo_fast,tc -s qdisc 确认真在跑 fq_codel。
  • RPKI 不合规前缀被丢:出公告前先跑 RPKI 验证,避免你的小前缀在某些上游直接被丢。
  • HKIX 对等不稳定:若对等短时 flap,确保 优先级不至于“磁吸”过去,对等 flap 时可被 Transit 平滑接管。

实施结果(复盘对比)

区域/运营商 调优前 RTT 中位 调优后 RTT 中位 抖动 p95 丢包 掉线率
Tokyo (IIJ) 32ms 28ms 2.1ms 0.2% 0.6% → 0.3%
Tokyo (KDDI) 47ms 34ms 3.4ms 0.5% 2.0% → 0.7%
Seoul (KT) 39ms 31ms 2.6ms 0.3% 0.8% → 0.4%
Seoul (SKB) 58ms 41ms 4.8ms 0.7% 1.4% → 0.6%

丢包、抖动一降,玩家体感“卡顿消失”的反馈第二天就回来了;最重要的是 跨局稳定(再匹配不会无故断开)。

可复用的上线清单(Checklist)

  1.  两家以上 Transit,明确“偏 JP / 偏 KR”的职责
  2.  FRR BGP:按 AS-Path 设置 local-pref;上游社区按文档确认
  3.  HKIX 对等/直连能拉则拉,健康度监控接入大盘
  4.  ELRepo kernel-ml,上 fq_codel,开启 BBR(用于 TCP 登录/下载)
  5.  NIC 队列、RPS/RFS、IRQ 绑核
  6.  UDP 生存包与无损迁移逻辑上线,房间状态双播切换
  7.  PMTUD、ICMP、防火墙与 conntrack 配置自检
  8.  Smokeping + PingMesh(东京/首尔多点)
  9.  预案:GRE/IPIP 旁路直通 + BFD

凌晨 02:40,我在机房走到窗边,湾仔的灯火还亮,Grafana 的红线终于躺平。Discord 里有人发“今天对局稳得离谱,像换了条网”。我们没换“服务器”,只是把 香港这张网的打法 换了:让“近路”真正变成“稳路”,让线路为业务让路。

如果你也把电竞平台放在香港,别只问“带宽多大”,更要问 “线路怎么打”——这篇实操笔记,够你把第一颗螺丝拧紧,也帮你在下一次深夜,少挨一点骂。