部署在香港服务器上的直播平台,如何用多线 BGP(CN2/CMIN2/CU/PCCW)分流直播流量,避免单线拥塞?
技术教程 2025-09-16 11:06 202


那天是周五晚高峰,直播间同时在线突破预期,单线网络(原本走的国际出口)开始抖动。OBS 推流端显示“Dropping frames”,客服群里炸了。

我们当场把预案打开:把 CN2 / CMIN2 / CU / PCCW 四条线全部拉满,按运营商与目的 AS 分流;通过 BGP 社区 + 本地优先级(local-preference)+ 更具体前缀(more specific)把入站(观众看流)和出站(主播推流)分摊出去。十几分钟后,丢包从 8% 掉到 <0.3%,直播评论区“不卡了”。
下面就是那次落地方案的完整复盘与可复用的配置清单。

1)目标与场景

  • 场景:直播平台部署在香港单 Region(低时延面向大陆用户),业务包括 SRT/RTMP 推流、转码、HLS/LL-HLS 分发(香港为源站/中间层,国内边缘 CDN 另配)。
  • 目标:通过 多线 BGP(CT/CN2、CMI/CMIN2、CU、PCCW)实现入站与出站的可控分流与快速故障切换,避免晚高峰单线拥塞。
  • 约束:操作系统统一 CentOS 7(内核建议升级)、生产无感切换(不中断直播)、变更可回滚、对主播与观众侧“透明”。

2)整体架构(ASCII 拓扑)

拓扑:
          ┌─────────────── Internet ────────────────┐
          │                                          │
      ┌───▼───┐   ┌───────▼───────┐   ┌───────▼───────┐   ┌───────▼───────┐
      │ CN2   │   │  CMIN2 (CMI)  │   │   CU (9929)  │   │   PCCW (3491) │
      └───▲───┘   └───────▲───────┘   └───────▲───────┘   └───────▲───────┘
          │                │                   │                   │
          ├──────────── BGP Upstreams (eBGP,BFD) ─────────────────┤
          │                │                   │                   │
                   ┌────────────────  Core ────────────────┐
                   │   EVPN/VLAN+LACP  |  TOR x2 (MLAG)    │
                   │   Route-Reflector |  Out-of-band Mgmt │
                   └───────▲───────────────────────▲───────┘
                           │                       │
                   ┌───────┴───────┐       ┌──────┴────────┐
                   │ Ingest(SRT)   │       │  Origin/HLS   │
                   │ Transcode GPU │       │   Cache/Edge  │
                   └───────▲───────┘       └──────▲────────┘
                           │                       │
                   ┌───────┴───────────────┐  ┌───┴──────────┐
                   │      FRR/BIRD (BGP)   │  │  Policy PBR  │
                   │ Healthcheck + ExaBGP  │  │(nftables mark)│
                   └───────────────────────┘  └───────────────┘

3)机房与硬件清单(我们实配过的安全保守选型)

角色 型号/CPU 内存 GPU 网卡 磁盘 OS 备注
Ingest/Transcode AMD EPYC 7443P (24c) 128GB NVIDIA T4 ×2 或 A10 ×1 Mellanox ConnectX-4 Lx 25G ×2 NVMe 1.92TB ×2 (RAID1) CentOS 7 + kernel-ml 5.4 NVENC、SRT/RTMP 入口
Origin/HLS Intel Xeon 4310 (12c) 128GB - Intel X710 10G ×2 NVMe 3.84TB ×2 (RAID1) CentOS 7 + kernel-ml HLS/LL-HLS 切片与缓存
网络核心/TOR Arista/Dell 交换机 - - 25G/10G 上联 - - MLAG,BFD 到上游
路由器(可用软路由) 2×通用 x86 32GB - 25G ×2 SSD CentOS 7/FRR eBGP 对接四家上游

为什么 kernel-ml 5.4? CentOS 7 默认内核对 25G/40G 驱动与 GRO/TSO 行为偏老,升级后中断亲和/多队列和BBR效果明显更稳。

4)运营商与地址规划

上游 ASN/产品 我们的用法 Commit 备注
CN2(CT) 常见 AS4809(CN2 GIA) 面向电信观众/主播的入出站主路 2–5G 起步 对大陆电信 RTT/丢包最友好,贵
CMIN2(CMI) 常见 AS58453(CMI) 面向移动用户的主路 2–5G 大陆移动质量更稳
CU(9929 优质线) AS9929(CU Premium)/AS4837(默认) 面向联通用户的主路 2–5G 尽量选 9929
PCCW AS3491 兜底与国际 1–2G 跨境回国可作备胎/补充

前缀规划(示例,使用 RFC5737 保留网段占位):

  • 公网段 A:203.0.113.0/23(观众访问域名 A 的 A 记录)
  • 203.0.113.0/24 专供 CN2(more specific 仅对 CN2 广告)
  • 203.0.114.0/24 专供 CMIN2/CU(对 CMI、CU 广告;PCCW 仅公告 /23)
  • 公网段 B:198.51.100.0/24(主播推流入口,SRT/RTMP)
  • 按运营商分池:不同域名解析到不同池,或用同池但 BGP 入站做拆分

要点:入站可用“更具体前缀 + 选择性通告”来控制流量进哪条线;出站靠 local-pref/AS 路径匹配,必要时配合 PBR。

5)BGP 设计要点

5.1 入站分流(观众→我们)

  • 对 CN2:仅向 CN2 通告 203.0.113.0/24,其余上游只通告 203.0.113.0/23。大多数路由器偏好更具体前缀,因此电信侧访问趋向走 CN2。
  • 对 CMI/CU:同理把 203.0.114.0/24只对 CMI/CU 通告。
  • 拥塞/故障:通过 ExaBGP 动态撤回某条线的 /24,保留 /23,由其他上游接管(观众最多几十秒路由收敛影响,通常 <10s)。

5.2 出站分流(我们→观众)

在 FRR/BIRD 中使用 AS-PATH/社区匹配:

  • 目的 AS 属于 4134/4809(CT) → 首选 CN2(local-pref 200)
  • 目的 AS 属于 9808/58453(CMI/移动) → 首选 CMIN2
  • 目的 AS 属于 9929/4837(CU/联通) → 首选 CU
  • 其他国际/未知 → 走 PCCW 或性价比最高的一条,local-pref 150

注意:不同上游支持的 BGP 社区(调优入口/出口、黑洞、优先)不一样,上线前向运营商要社区手册。本文示例社区值仅作占位。

5.3 健康检查与快速切换

BFD:对每条 eBGP 会话启用 bfd(如 min_rx/tx 200ms, multiplier 3),链路级故障秒级感知。

业务质量检查:以省会城市拨测(Ping/HTTP 拉流)统计 RTT、抖动、丢包;超过阈值触发 ExaBGP 调整:

  • 提高 MED/AS-PATH prepend 或撤回 more specific /24
  • 动作可按省/运营商维度执行,避免全网震荡

6)关键配置(可直接落地)

6.1 CentOS 7 内核与网络优化(/etc/sysctl.d/99-tune.conf)

net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
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_congestion_control = bbr
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_mtu_probing = 1
net.ipv4.udp_rmem_min = 16384
net.ipv4.udp_wmem_min = 16384
net.core.default_qdisc = fq

配合 irqbalance、多队列 RSS/ RPS;必要时关闭 GRO/LRO 检测是否减少重排。

6.2 FRR(bgpd.conf)——四上游 eBGP

router bgp 65001
 bgp router-id 203.0.113.2
 no bgp default ipv4-unicast
 timers bgp 5 15
 bgp log-neighbor-changes

# CT/CN2
 neighbor 203.0.113.253 remote-as 4809
 neighbor 203.0.113.253 description CT-CN2
 neighbor 203.0.113.253 timers 5 15
 neighbor 203.0.113.253 bfd

# CMI/CMIN2
 neighbor 203.0.113.254 remote-as 58453
 neighbor 203.0.113.254 description CMI-N2
 neighbor 203.0.113.254 timers 5 15
 neighbor 203.0.113.254 bfd

# CU/9929
 neighbor 203.0.113.252 remote-as 9929
 neighbor 203.0.113.252 description CU-9929
 neighbor 203.0.113.252 timers 5 15
 neighbor 203.0.113.252 bfd

# PCCW
 neighbor 203.0.113.251 remote-as 3491
 neighbor 203.0.113.251 description PCCW
 neighbor 203.0.113.251 timers 5 15
 neighbor 203.0.113.251 bfd

 address-family ipv4 unicast
  network 203.0.113.0/23
  # more specific:按上游有选择地在 route-map 中permit
  network 203.0.113.0/24 route-map ANNOUNCE-CT
  network 203.0.114.0/24 route-map ANNOUNCE-CMI-CU

  neighbor 203.0.113.253 activate
  neighbor 203.0.113.254 activate
  neighbor 203.0.113.252 activate
  neighbor 203.0.113.251 activate

  # 出站策略:按目的AS优先哪条线
  neighbor 203.0.113.253 route-map OUT-SET-CT out
  neighbor 203.0.113.254 route-map OUT-SET-CMI out
  neighbor 203.0.113.252 route-map OUT-SET-CU out
  neighbor 203.0.113.251 route-map OUT-SET-PCCW out

  # 入站选择:防止上游把垃圾路由喂给你
  neighbor 203.0.113.253 route-map IN-FILTER in
  neighbor 203.0.113.254 route-map IN-FILTER in
  neighbor 203.0.113.252 route-map IN-FILTER in
  neighbor 203.0.113.251 route-map IN-FILTER in
 exit-address-family

ip as-path access-list CT permit _4134$|_4809$
ip as-path access-list CMI permit _9808$|_58453$
ip as-path access-list CU permit _9929$|_4837$

route-map OUT-SET-CT permit 10
 set local-preference 200
route-map OUT-SET-CMI permit 10
 set local-preference 190
route-map OUT-SET-CU permit 10
 set local-preference 190
route-map OUT-SET-PCCW permit 10
 set local-preference 150

# 仅 CT 宣告 /24
route-map ANNOUNCE-CT permit 10
 match interface eth0
# 仅 CMI/CU 宣告 /24
route-map ANNOUNCE-CMI-CU permit 10
 match interface eth1

# 入站过滤(示例)
route-map IN-FILTER permit 10
 match ip address prefix-list DEFAULTS
 set ip next-hop unchanged

ip prefix-list DEFAULTS seq 5 permit 0.0.0.0/0 le 0

注:ANNOUNCE-* 的“match interface”是一个简易占位,你可改为 neighbor X.X.X.X route-map X out 并在 route-map 里用 set community 实现更细策略;具体 BGP 社区值向上游索要官方表。

6.3 ExaBGP 动态调度(质量差时撤回 /24)

/etc/exabgp/exabgp.env(简化示例):

neighbor 203.0.113.253 {
  router-id 203.0.113.3;
  local-address 203.0.113.3;
  local-as 65001;
  peer-as 4809;
  api {
    processes [ quality ];
  }
}

process quality {
  run /usr/local/bin/quality_watchdog.py;
}

quality_watchdog.py(伪代码思路):

  • 每 5 秒读取拨测结果(如 Redis/Prometheus API);
  • 若 CT 丢包 >1% 持续 30s:向 ExaBGP 打印 withdraw route 203.0.113.0/24 next-hop self; 恢复稳定后 announce route ...。
  • 同理对 CMI/CU 执行。

6.4 可选:PBR(nftables 标记 + 多路由表)

当需要“应用/端口级别”强制出某条线时(比如 SRT 必走 CN2):

# 标记 SRT/UDP 流量
nft add table inet pbr
nft 'add chain inet pbr mangle { type route hook output priority mangle; }'
nft 'add rule inet pbr mangle udp dport 10000-20000 meta mark set 0x1'

# 策略路由表
ip rule add fwmark 0x1 table 101
ip route add default via 203.0.113.253 dev eth0 table 101   # CN2 下一跳

7)直播栈落地

7.1 Ingest:SRT/RTMP(SRS 示例)

srs.conf 关键片段:

listen              1935;        # RTMP
http_api {
    enabled         on;
    listen          1985;
}
srt_server {
    enabled         on;
    listen          10080;
}
vhost __defaultVhost__ {
    tcp_nodelay     on;
    min_latency     on;
    play {
        gop_cache   on;
    }
    publish {
        mr { enabled on; } # 合并转推,压低抖动
    }
}

7.2 转码(NVENC)

ffmpeg -hwaccel cuda -c:v h264_cuvid -i rtmp://ingest/app/stream \
 -filter_complex "[0:v]scale=1280:720:flags=lanczos[v1];[0:v]scale=1920:1080[v2]" \
 -map "[v1]" -c:v h264_nvenc -preset p5 -b:v 3000k -maxrate 3500k -bufsize 6000k -g 48 \
 -map "[v2]" -c:v h264_nvenc -preset p5 -b:v 6000k -maxrate 7000k -bufsize 12000k -g 48 \
 -map a:0 -c:a aac -b:a 128k \
 -f hls -hls_time 2 -hls_list_size 6 -hls_flags delete_segments+independent_segments \
 /var/hls/stream/index.m3u8

7.3 HLS/LL-HLS(Nginx)

worker_processes auto;
events { worker_connections  65535; }

http {
    sendfile on; tcp_nopush on; tcp_nodelay on;
    keepalive_timeout  65;
    aio threads;

    server {
        listen 80 reuseport;
        location /hls/ {
            types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; }
            root /var;
            add_header Cache-Control "max-age=1";
        }
    }
}

8)质量拨测与阈值

省会拨测面板(样例数据,单位 ms/%)

线路 北京 上海 广州 成都 丢包(晚高峰)
CN2 36 32 24 45 0.2%
CMIN2 42 38 29 48 0.4%
CU(9929) 45 35 28 50 0.5%
PCCW 70 62 56 78 1.2%

告警规则(示意)

  • 任一线路 1 分钟内丢包均值 > 1% 且 RTT 中位数上升 > 25% → 触发 EXABGP_WITHDRAW(/24)
  • 所有线路均 >1% → 开启 HLS 切片扩容 与 跨池调度,DNS 限流(见下)

9)DNS/域名策略(可选但很好用)

  • 观众域名:play.example.com
  • 电信 LocalDNS → 解析到 A: 203.0.113.x(CN2 池)
  • 移动 LocalDNS → 解析到 A: 203.0.114.x(CMI/CU 池)
  • 主播推流域名:push-ct.example.com / push-cm.example.com …(显式分线)
  • 工具:权威 DNS(PowerDNS/gdnsd/NS1/Route53)+ EDNS Client Subnet(谨慎配置)

有了 DNS 粗分流 + BGP 精调,晚高峰抗压会更稳。

10)上线当天我们踩过的坑(与解决过程)

  • 上游不接受 /25:一开始想做更细粒度,结果多家过滤了 /25,最小接受 /24。当场把策略改成 /23 + 两个 /24 组合。
  • 社区值不匹配:参考了旧文档发送了 no-export-to-163 一类社区,不生效。打电话找 NOC 拿到了最新社区手册(每家都不同),改完 5 分钟收敛。
  • BFD 过敏:min_rx/tx 50ms, multiplier 3 过于激进,偶发抖动导致来回 flap。调成 200ms/3 + 抖动抑制(hold-down 30s)。
  • SRT 丢包与 NIC offload:T4 机器上 GRO 打开时 UDP 重排增多,SRT 时延偶抖。对 SRT 接口关闭 gro/lro 解决。
  • conntrack 爆表:LL-HLS 小切片 + 大并发,nf_conntrack_max 不够,且 HLS 静态 GET 没必要过 conntrack。对 /hls/ 出口 绕过 conntrack(nft ct state 规则)+ 增大表项。
  • ECMP Hash 不稳定:上游做了 per-flow ECMP,源端口固定导致个别流被分到拥塞路径。我们在 SRT 推流侧启用 pbkeylen + transtype 变化端口范围,效果好转。
  • MTU 黑洞:CMI 某段链路 MTU 1500 实际被隧道压缩,导致 PMTU 探测失败。启用 tcp_mtu_probing=1 并在该上游接口降 MTU 至 1472,稳定。

11)变更与回滚 Runbook(摘录)

  • T-24h:从低峰开始宣告 /24,观察 2 小时;拨测通过。
  • T-2h:开启 BFD;ExaBGP 以 只记录不执行 模式跑 30 分钟,无误再放权。
  • 上线:逐线路放开 ANNOUNCE-*,Grafana 观众丢包 <0.5% 即视为稳定。
  • 回滚:withdraw /24 回到 /23 兜底;关闭 ExaBGP;local-pref 全部 150。

12)成本与容量(粗估)

项目 单价/区间 备注
CN2 带宽 ¥ 300–600 / Mbps 视供应商与承诺期
CMIN2/9929 ¥ 150–400 / Mbps 波动大
PCCW ¥ 80–200 / Mbps 国际为主
25G 专线端口 ¥ 2k–5k / 月 部分含 BFD
服务器(转码) ¥ 1.5–2.5w / 月 含 T4/A10

经验法则:电信与移动各占 35–40% Commit,联通 15–20%,PCCW 10–15% 作兜底;高峰按 1.6× 均值 预留。

13)实测对比(上线前后)

指标 上线前(单线) 上线后(多线+BGP)
晚高峰观众丢包(P50) 3.5% 0.4%
直播开始首帧时间 P95 3.2s 1.7s
推流端回报码率抖动 频繁 偶发
NOC 手工干预 每晚 3–5 次 偶发(自动化处理)

14)安全与合规提示

与上游签订 RTBH/黑洞 社区支持,防 DDoS。

观众日志与主播数据分区存储,注意 个人信息与合规。

跨境合规与备案:边缘 CDN 与内地服务分开算,域名解析策略合规评估。

15)夜里两点的那通电话

上线那天,机房的空调出风像永远吹不完的北风。Grafana 的曲线慢慢贴近我们预期的“平线”。凌晨两点,主播群里有人发了句“今天不卡了,兄弟们辛苦”。
我把最后一条 withdraw 变更记录上链,给自己倒了杯凉掉的咖啡。多线 BGP 的意义,大概就在于:当一条路堵了,你能在最短时间打开另一条——而观众只会觉得“今晚的网挺稳”。

附:可直接复用的清单

  •  向四家上游索要最新 BGP 社区文档与最小可接受前缀长度
  •  申请至少一个 /23,并能分拆成两个 /24
  •  FRR/BIRD 基础配置、BFD、max-prefix、防御性过滤
  •  ExaBGP + 质量拨测脚本(按省/运营商维度)
  •  DNS(EDNS Client Subnet 可选)+ 分线域名
  •  CentOS 7 kernel-ml、irqbalance、ethtool、sysctl 优化
  •  SRS/FFmpeg/NGINX HLS 生产参数与监控
  •  回滚预案与变更窗口

如果你也准备在香港做面向大陆的直播,中短期内,这套“DNS 粗分流 + BGP 精调 + 动态撤回”的组合拳,依旧是最好用、最不容易踩大坑的工程路径。