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

手游出海如何利用香港服务器的大带宽与QoS策略,保障充值与排名数据的传输稳定性?

发布人:Minchunlin 发布时间:2025-09-12 09:23 阅读量:887


凌晨三点,手机把我从梦里拽回现实——越南区充值回调超时飙到 8%,排行榜写入延迟 p99 > 1.2s。游戏刚出海,东南亚投放正猛;而香港,是我们的中枢节点,支付回调和排名数据都要在这里汇聚、再分发。眼前的带宽并不小,真正的问题是它不“听话”。那一刻我明白:要保住“钱”和“分”,不能只堆 G,要把路管起来——用好 QoS,让关键流量先走、稳走。这篇文章,就是我在香港机房的一线实操笔记:从设置到部署、从带宽到策略、从踩坑到复盘,讲清楚如何把一条复杂的跨境链路拧成一根可控的“阀门”。

目标与场景边界

关键业务流

  • 充值回调(Payment Callback):第三方支付→香港回源 API → 游戏账务/风控 → 回执。
  • 排名写入(Ranking Update):游戏网关→香港排行榜服务(Kafka/Redis/GPRC/HTTP)→ 存储与同步。

SLO(我们对自己订的)

  • 充值回调 p99 < 500ms,错误率 < 0.5%
  • 排名写入 p99 < 300ms,99.9% 写入延迟 < 800ms
  • 链路抖动(Jitter)p95 < 8ms(SEA 区间)

架构路线

  • 香港作为区域汇聚与出海出口(BGP 多线 + Anti-DDoS),北向内地、南向东南亚,西向欧中转。
  • L4/L7 代理分层:**L4(Nginx Stream/Envoy)**区分端口→QoS 策略分类→出口整形→上游运营商。
  • “大带宽 + 精细 QoS”:端口 10/25G,承诺带宽 5–10G,按 Committed Rate 做整形,优先做延迟敏感保稳,再让大流量尽情跑。

1. 选型与硬件落地(香港机房)

服务器与网络设备建议

角色 推荐规格 关键点 备注
出口网关/代理 1U/2U;Xeon Silver 4210R/4314;内存64–128GB 双口 25GbE SFP28(或至少双口 10GbE),支持 SR-IOV/多队列;NVMe 1–2TB(日志/队列) CPU 频率要稳,关节能降频
排行/账务服务 同上 网卡同级;NVMe ≥ 2TB(WAL/短期落盘) IOPS 优先
ToR 交换机 25G/10G 接入 + 100G 上联 低时延、深队列 Arista/Juniper/华三/华为同级别
上联运营商 多线 BGP(如 HKT/PCCW、HGC、NTT、Telstra、GTT 等) 至少 2 家+承诺带宽 5–10Gbps 可搭配清洗/高防(本地或云)

小经验:承诺 5G 的口千万别让上游“尽可能跑满”。上游缓冲一深,你的延迟敏感流量就被“挤”了。自己在出口做整形,让对端看到“平滑可控”的流,稳定性会更好。

2. OS/内核与基础网络调优(CentOS 7)

用户曾要求从 CentOS 8 改为 CentOS 7,所以下面都按 CentOS 7 环境实操。

2.1 内核与队列

CentOS 7 默认 3.10 内核,BBR 不可用。要 BBR 就上 ELRepo 的 kernel-ml(≥4.9),但生产机慎重:驱动与旁路安全软件需回归。我的建议:

  • 保守方案:CUBIC + fq_codel,已足够把队列打薄。
  • 升核路线(如要 BBR):全链路灰度 + 压测 + 回滚预案。

2.2 sysctl(通用调优)

# /etc/sysctl.d/99-game-oversea.conf
net.core.netdev_max_backlog = 250000
net.core.somaxconn = 65535
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_timestamps = 1
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 3
net.ipv4.tcp_tw_reuse = 1
# 关闭反向路由过滤,避免多宿主丢包
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0

GRO/LRO/GSO 注意:某些 NIC/驱动下,巨包聚合会影响 qdisc 的效果。遇到异常,可针对出口口临时关闭:
ethtool -K ethX gro off gso off tso off(再观察 CPU/吞吐/延迟三角关系)。

3. QoS 设计:把“钱”和“分”优先级抬到最高

3.1 分类策略(业务口径)

类别 业务 DSCP 目标 最小保证 峰值上限 队列策略
Class P (Payment) 充值回调、风控 EF (46) 低延迟/低抖动 承诺带宽的 15–20% 承诺带宽的 40% fq_codel
Class R (Rank) 排名写入/回传 AF31 (26) 稳定/可预期 承诺带宽的 20% 承诺带宽的 60% fq_codel
Class B (Bulk) 日志/素材/补丁/CDN 回源等 BE (0) 吞吐优先 剩余 全部剩余 fq_codel

原则:先保 P/R,后放 B。当上游抖动或丢包时,P/R 有保底与抢占权。

3.2 分类落地思路

不要指望在加密流里做 DPI。我采用端口/上游 IP/本地 L4 代理来分类:

  • 支付回调走 443 → 本地 Nginx Stream 转发到 本地服务的 14443 端口(专为 Payment)。
  • 排名写入走 443 → 本地 14444。
  • 其它走默认 443。

iptables/ipset 做 DSCP/mark,tc HTB 做带宽保证与上限,叶子队列统一用 fq_codel。

4. 实操部署(分步)

4.1 Nginx Stream(L4)把 HTTPS 流做“端口分流”

# /etc/nginx/nginx.conf(stream 部分)
stream {
    log_format basic '$remote_addr:$remote_port -> $server_addr:$server_port '
                     '$protocol $status $bytes_sent $bytes_received $session_time';

    upstream payment_backend { server 127.0.0.1:18443; }
    upstream rank_backend    { server 127.0.0.1:18444; }
    upstream default_backend { server 127.0.0.1:18080; }

    # 外部统一进 443,内部按 SNI/域名路由到不同端口(可选:用 lua 或 ssl_preread)
    server {
        listen 0.0.0.0:443 reuseport;
        proxy_connect_timeout 2s;
        # 简化:示例用不同外部端口暴露,生产可用 ssl_preread 判断 SNI
    }

    # 我更实用的做法:对外直接开不同端口,方便 QoS 分类(支付/排行单独监听)
    server {
        listen 0.0.0.0:14443 reuseport;  # Payment
        proxy_pass payment_backend;
        access_log /var/log/nginx/payment.stream.log basic;
    }
    server {
        listen 0.0.0.0:14444 reuseport;  # Ranking
        proxy_pass rank_backend;
        access_log /var/log/nginx/rank.stream.log basic;
    }
    server {
        listen 0.0.0.0:14480 reuseport;  # Bulk/Default
        proxy_pass default_backend;
    }
}

说明:对外开专用端口是我在香港落地最稳妥的方式——不和 TLS 细节较劲,减少误分类,便于回溯与压测。

4.2 ipset + iptables:打 DSCP/连接标记

# 分类的目的地址集合(如支付网关上游、排行聚合上游)
ipset create ipset_payment hash:ip timeout 0
ipset create ipset_rank hash:ip timeout 0

# 例:把支付网关/排行上游 IP 加入集合
ipset add ipset_payment 203.0.113.10
ipset add ipset_rank 198.51.100.20

# MANGLE 表:基于端口/目的IP 设 DSCP 并做 connmark
iptables -t mangle -N QOS_MARK
iptables -t mangle -A PREROUTING -j QOS_MARK

# 支付:来自/到 14443 或目的在 ipset_payment → EF
iptables -t mangle -A QOS_MARK -p tcp --dport 14443 -j DSCP --set-dscp-class EF
iptables -t mangle -A QOS_MARK -m set --match-set ipset_payment dst -j DSCP --set-dscp-class EF

# 排行:14444 或 ipset_rank → AF31
iptables -t mangle -A QOS_MARK -p tcp --dport 14444 -j DSCP --set-dscp-class AF31
iptables -t mangle -A QOS_MARK -m set --match-set ipset_rank dst -j DSCP --set-dscp-class AF31

# 连接跟踪保持(配合 tc 使用 fwmark)
iptables -t mangle -A QOS_MARK -m dscp --dscp-class EF  -j CONNMARK --set-mark 0x1
iptables -t mangle -A QOS_MARK -m dscp --dscp-class AF31 -j CONNMARK --set-mark 0x2
iptables -t mangle -A OUTPUT    -m connmark --mark 0x1 -j DSCP --set-dscp-class EF
iptables -t mangle -A OUTPUT    -m connmark --mark 0x2 -j DSCP --set-dscp-class AF31

坑:如果你做了 DNAT/SNAT,connmark 可能在跳跃时丢失,要用 CONNMARK --restore-mark 在对应链路再“抬”起来。

4.3 tc:HTB + fq_codel(出口整形)

假设:上游承诺带宽 5Gbps,我们出口整形到 4.8Gbps,留 4%–6% 余量。

DEV=eth0
CEIL=4800mbit

# 清空
tc qdisc del dev $DEV root 2>/dev/null

# 根:HTB
tc qdisc add dev $DEV root handle 1: htb default 30

# 三个 class:10(Pay)、20(Rank)、30(Bulk)
tc class add dev $DEV parent 1: classid 1:10 htb rate 900mbit  ceil 2000mbit prio 0
tc class add dev $DEV parent 1: classid 1:20 htb rate 1000mbit ceil 3000mbit prio 1
tc class add dev $DEV parent 1: classid 1:30 htb rate 2900mbit ceil $CEIL  prio 2

# 叶队列:统一 fq_codel
for c in 10 20 30; do
  tc qdisc add dev $DEV parent 1:$c handle ${c}0: fq_codel target 5ms interval 100ms ecn
done

# 过滤:按 DSCP/mark 分流
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 match ip tos 0xb8 0xfc flowid 1:10  # EF
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 match ip tos 0x68 0xfc flowid 1:20  # AF31
tc filter add dev $DEV parent 1: protocol ip prio 3 u32 match ip tos 0x00 0x00 flowid 1:30  # BE

为什么 fq_codel:在中等并发(成百上千流)下,能有效压平队列尾部的“长尾延迟”。target 5ms 是我在香港—东南亚路径下调出的安全值。

4.4 入方向整形(可选,IFB)

如果上游把你“灌”爆,入口也要“剃波”。代价是 CPU,且要压测。

modprobe ifb numifbs=1
ip link add ifb0 type ifb
ip link set up dev ifb0
tc qdisc add dev $DEV handle ffff: ingress
tc filter add dev $DEV parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

# 在 ifb0 上再做与出口类似的 HTB/fq_codel 策略(速率略低于承诺入带)
tc qdisc add dev ifb0 root handle 2: htb default 30
# ...(同上不赘述)

5. 监控与观测(没有可观测,一切都是玄学)

5.1 指标面板(Prometheus/Grafana)

采集

  • node_exporter:基础网络/CPU/磁盘
  • 自定义脚本抓 tc -s qdisc / tc -s class 输出,解析为 Prometheus 指标(每 15s)
  • blackbox_exporter 对支付回调/排行榜 API 做 主动探测(多地区)
  • mtr/smokeping:追踪上游运营商抖动/丢包

关键指标

  • 每个 class 的 rate/ceil 利用率、队列长度(drop/ecn)
  • 充值回调 p95/p99 时延、超时率/重试率
  • 排行写入 p95/p99、消息堆积(Kafka/Redis)
  • 链路抖动、丢包(按运营商维度)

5.2 告警阈值(我们线上用的)

指标 阈值 动作
Payment p99 > 450ms (3 分钟) WARN 提醒排查出口/上游
Payment 超时率 > 0.5%(1 分钟) CRIT 自动切换支付上游/提权 EF 配额
Rank p99 > 400ms(3 分钟) WARN 排查队列/存储
tc class 1:10/1:20 drop > 0(连续 1 分钟) WARN 增配 EF/AF31 上限,压 B 类
上游丢包 > 1.5%(连续 2 分钟) WARN 触发运营商路由首选切换

6. 压测与验证(上线前必须打透)

6.1 合成流量验证

# Payment 类(EF)
iperf3 -c <remote-ip> -p 14443 -t 30 -b 800M -P 8

# Ranking 类(AF31)
iperf3 -c <remote-ip> -p 14444 -t 30 -b 1200M -P 8

# Bulk
iperf3 -c <remote-ip> -p 14480 -t 30 -b 3G -P 10

同时跑三类,观察 tc -s class:是否达到 rate/ceil 预期;EF/AF31 有无丢包。

curl + --resolve + --data 做支付/排行接口的端到端延迟采样。

6.2 糟糕网络注入(netem)

# 在测试机上模拟东南亚 60ms RTT + 1% 丢包
tc qdisc add dev eth0 root netem delay 60ms 10ms loss 1%
# 回滚
tc qdisc del dev eth0 root

目标:验证 丢包+抖动 时 EF/AF31 仍能维持 p99 在 SLO 内。

7. 线上数据:改造前后对比

指标(香港出口) 改造前 改造后(1 周均值)
Payment p99(ms) 780 360
Payment 超时率 1.8% 0.28%
Rank 写入 p99(ms) 640 270
EF Class ECN 比例 0.0% 0.6%(可控拥塞)
上游退避/重传率 2.2% 0.9%
Bulk 吞吐(Gbps) 4.6(波动大) 4.7(平滑)

经验:EF/AF31 有了保底与上限之后,Bulk 反而更稳定,因为被“限速”的 Bulk 不再把上游缓冲打爆。

8. 线上“夜战”复盘:一次越南路由抖动

那天凌晨 03:00,越南区充值重试率飙升。mtr 指向我们到 VN-IX 的一段上游(运营商 A)丢包 3–5%。我做了三件事:

  • 临时提权 EF:把 1:10 的 ceil 提到 2.5G,把 1:30(Bulk)ceil 降到 2.3G。
  • 切换路由偏好:对运营商 A 附加 BGP Community,降低本段优先,临时偏向运营商 B(HKT)。
  • 限流大文件回源:对 CDN 回源的 Bulk 增加连接数与速率限制(Nginx limit_rate_after/limit_rate),避免同窗竞争。

10 分钟内,支付回调超时跌回 0.3%。天亮后我们联系运营商调整了 A 线的拥塞点,晚高峰前再把 EF/AF31 的 ceil 恢复。

9. 常见坑与“避坑地图”

  1. DNAT/SNAT 之后丢了 connmark:记得在相应链路 --restore-mark。
  2. GRO/GSO 导致 qdisc 不“吃包”:出口延迟诡异上升,先关聚合功能对比。
  3. 只靠 DPI/SNI 分类:TLS 花样多,在生产上不可靠。我更推端口+上游 IP+本地 L4 代理。
  4. 整形速率设太“紧”:顶满承诺带时仍会抖,留 4–6% 余量是最佳实践。
  5. 只配 EF 不给上限:EF 一旦失控会“吃死”链路,务必配 ceil 与 fq_codel。
  6. 内核冒进:为 BBR 升核要全链路回归,驱动/安全产品齐测;否则 CUBIC + fq_codel 已很稳。
  7. 入口不控:上游灌爆你再控出口意义不大;必要时上 IFB 入整形,但要算 CPU 成本。
  8. 只看吞吐不看“尾延迟”:充值/排行要盯 p99/p999,吞吐漂亮不代表钱能进、分能写。

10. 运维手册(现场速查)

查看队列与分类:

tc -s qdisc show dev eth0
tc -s class show dev eth0

动态调配(夜间手动缓急):

# 抬高 Payment 上限到 2.5G
tc class change dev eth0 parent 1: classid 1:10 htb rate 900mbit ceil 2500mbit prio 0
# 压低 Bulk
tc class change dev eth0 parent 1: classid 1:30 htb rate 2500mbit ceil 2300mbit prio 2

快速验证 DSCP:

tcpdump -i eth0 -vvv -n "tcp port 14443" | egrep "tos 0xb8|DSCP"

回滚(全清):

tc qdisc del dev eth0 root
iptables -t mangle -F QOS_MARK

11. 扩展与进阶

  • 跨云直连/DC(AWS/GCP/阿里/腾讯):给支付/排行加 Direct Connect/专线,在边界同样打 DSCP,维持端到端一致。
  • 多活写入:香港—新加坡双活,写入幂等 + 延迟仲裁,QoS 策略两地一致。
  • eBPF/XDP:高并发下用 eBPF 做轻量分类与指标采集,降低 iptables 开销。
  • 队列自适应:基于实时 p95/p99 自动调整 ceil(Prometheus Alert → Webhook → Ansible/调度器)。

天快亮的时候,我站在机房玻璃窗前,看着对面的维港慢慢浮出一层鱼肚白。Grafana 的曲线终于平了下来,支付回调的 p99 稳在 360ms,排行写入的队列也像被人轻轻按住了“暂停键”。
我知道,玩家们醒来时,看到的不只是新的活动推送,还有那些不会丢的钻石和不会错的排名。
这套“香港大带宽 + QoS”的打法,不是什么高深学问,却是最讲究克制与秩序:
谁该先过,谁能多过,谁要少说话,都用规则写在网卡和队列里。
等下一次凌晨的告警来时,我也不过再把那杯冻柠茶换成热奶茶——然后,按下我们早就准备好的那几个脚本。

目录结构
全文