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

直播平台如何利用香港服务器的CN2专线和国际大带宽线路保障国内和东南亚观众“秒开”?

发布人:Minchunlin 发布时间:2025-09-23 09:28 阅读量:738


我真正下定决心把香港直播集群“拉到极致”,是在一个周五晚高峰。那晚活动间同时在线超过 18 万,东南亚一切顺滑,国内北上广“秒开率”却从 97% 掉到 91%,弹幕里开始有人吐槽“要么黑屏要么卡在转圈”。我在将军澳机房的走道里,抱着笔电靠着机柜门当工位,一边看 mtr 曲线一边跟线路商打电话,最后定位到问题:国内回程绕了 163,抖动 60+ms。那晚我们临时切到 CN2 GIA 口、调低 GOP、上了 HTTP-FLV 的 GOP 缓存,秒开率几分钟内回到 98%+。

这篇文章就是把当晚的“救火”固化为体系化方案:用香港双口(CN2 + 国际大带宽)+ 策略路由 + SRS/Nginx 播放栈 + GeoDNS/HTTPDNS 调度 + 一揽子内核与编码优化,把“秒开”变成日常。

目标与指标

  • 核心目标:国内与东南亚用户首帧时间 ≤ 1.5s,95 线“秒开”(≤ 1s)≥ 97%,卡顿率(rebuffer ratio)≤ 1.5%。
  • 约束环境:操作系统 CentOS 7(用户侧有该要求),上新内核启用 BBR。
  • 协议栈:推流 RTMP;播放 HTTP-FLV(Web)/HLS(iOS);对极低延迟间使用 WebRTC(可选)。

网络路径:

  • 面向国内:China Telecom CN2 GIA(低抖动,低丢包,回程可控)。
  • 面向东南亚:国际大带宽口(如 PCCW/NTT/Tata/Equinix Fabric 互联)。
  • 调度方式:GeoDNS + HTTPDNS 双轨,优先本地解析,其次 App 走 HTTPDNS 兜底。

参考架构(文字版)

[主播端] --RTMP--> [Ingress 边缘(SRS Origin, HK)]
                        |  本地GPU/CPU转码
                        |---> [SRS Edge 集群, HK]
                        |---> [Relay 节点, 新加坡/曼谷 (可选)]
                         \
                          \--(HTTP-FLV/HLS via Nginx)-> 观众

[DNS 调度] GeoDNS/HTTPDNS -> 国内解析到 CN2 IP;东南亚解析到国际口 IP
[网络] 同一台或同集群服务器双上联:CN2 (ens33) + 国际口(ens34)
[路由] 基于源地址的策略路由,确保流量从对应上联进出
[监控] Prometheus + node_exporter + blackbox_exporter + SRS API

资源与规划

1) 机型与参数(示例)

角色 机房 线路 型号 CPU 内存 存储 GPU 网卡 峰值并发
Ingress/Origin HK CN2 + Intl 2U 2×Xeon 6248R 192GB 2×1.92TB NVMe 1×T4(选) 2×10GbE 推流 5k, 观众 30k
Edge-1 HK CN2 1U 1×EPYC 7543P 128GB 2×1.92TB NVMe - 2×10GbE 5–8 万并发
Edge-2 HK Intl 1U 1×EPYC 7543P 128GB 2×1.92TB NVMe - 2×10GbE 5–8 万并发
Relay-SG 新加坡 Intl 1U 1×EPYC 7443P 64GB 2×960GB NVMe - 2×10GbE 3–5 万并发

备注:T4 仅用于高并发转码(NVENC),纯转发可不配。

2) 网络与策略路由规划(示例)

接口 用途 线路 公网 IP 路由表 ID 备注
ens33 国内播放/推流回源 CN2 203.0.113.10/29 10 国内 A 记录解析到此 IP
ens34 东南亚播放 Intl 198.51.100.20/29 20 东南亚 A 记录解析到此 IP
lo:flv-cn 绑定服务 CN2 回源/回包 203.0.113.11 - 仅本地绑定
lo:flv-sea 绑定服务 Intl 回源/回包 198.51.100.21 - 仅本地绑定

系统初始化(CentOS 7)

目标:上新内核、BBR、文件句柄与队列、IRQ/队列绑定,保证在高并发下不“憋气”。

升级内核(ELRepo kernel-ml 5.x)并启用 BBR

yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-ml
grub2-set-default 0 && grub2-mkconfig -o /boot/grub2/grub.cfg
reboot

内核参数(/etc/sysctl.d/99-live.conf)

net.core.somaxconn = 65535
net.core.netdev_max_backlog = 262144
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

# 多路径下返程安全
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2

sysctl --system

文件句柄与进程数

echo '* soft nofile 1048576
* hard nofile 1048576
* soft nproc  65535
* hard nproc  65535' > /etc/security/limits.d/99-nofile.conf

IRQ 亲和与 RPS/XPS(以 10GbE 为例)

# 绑定网卡中断到特定 CPU,避免抖动(示意)
for i in /proc/irq/*/eth*/; do echo 2 > $i/smp_affinity; done

# 打开 RPS(按队列数调整)
echo ffffffff > /sys/class/net/ens33/queues/rx-0/rps_cpus
echo ffffffff > /sys/class/net/ens34/queues/rx-0/rps_cpus

双上联策略路由(确保进出对口)

要点:基于源地址的策略路由最稳。国内用户解析到 CN2 的 IP,请求到达后服务端用该“本机源地址”回复,自然通过 CN2 表走 CN2 口;东南亚同理。

创建路由表(/etc/iproute2/rt_tables)

10 cn2
20 intl

静态默认路由

ip route add default via 203.0.113.1 dev ens33 table cn2
ip route add default via 198.51.100.1 dev ens34 table intl

策略规则(根据本机源地址)

ip rule add from 203.0.113.10/32 table cn2
ip rule add from 198.51.100.20/32 table intl
ip rule add from 203.0.113.11/32 table cn2
ip rule add from 198.51.100.21/32 table intl

持久化(/etc/rc.local)

#!/bin/bash
ip route add default via 203.0.113.1 dev ens33 table cn2
ip route add default via 198.51.100.1 dev ens34 table intl
ip rule add from 203.0.113.10/32 table cn2
ip rule add from 198.51.100.20/32 table intl
ip rule add from 203.0.113.11/32 table cn2
ip rule add from 198.51.100.21/32 table intl
chmod +x /etc/rc.d/rc.local

坑 1:rp_filter 默认为 1 会丢非对称返回包,务必设置为 2(loose)。

坑 2:MTU 不一致会导致分片重传,先 ping -M do -s 1472 目标 校验路径 MTU。

播放栈部署(SRS + Nginx)

1) 安装 SRS(Simple Realtime Server)

yum groupinstall -y "Development Tools"
yum install -y git cmake gcc-c++ openssl-devel
cd /opt && git clone https://gitee.com/ossrs/srs.git
cd srs/trunk
./configure --jobs=4 --with-hls --with-http-server --with-nginx --with-ssl
make -j4 && make install

2) SRS 核心配置(/usr/local/srs/conf/srs.conf,精简示例)

listen              1935;
max_connections     100000;
daemon              on;
srs_log_tank        file;
srs_log_file        /var/log/srs.log;

http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}

vhost __defaultVhost__ {
    tcp_nodelay     on;
    min_latency     on;

    # 启动 GOP 缓存,HTTP-FLV 首屏秒开关键
    gop_cache       on;
    queue_length    10;

    # RTMP 推流入口
    chunk_size      60000;

    # HLS(iOS)
    hls {
        enabled         on;
        hls_path        ./objs/nginx/html/hls;
        hls_fragment    1;      # 1s 切片
        hls_window      3;
        hls_cleanup     on;
    }

    # HTTP-FLV(Web)
    http_remux {
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
        hstrs       on;     # 减少启动请求
    }

    # 转码(示例 1080p -> 720p/480p ABR)
    transcode live {
        enabled     on;
        ffmpeg      /usr/bin/ffmpeg;
        engine h264_720p {
            vfilter     {vfps=25,scale=1280:-2}
            vcodec      libx264
            vbitrate    2500
            vprofile    main
            vpreset     veryfast
            vparams     {keyint=50:sc_threshold=0}   # 2s GOP,保障秒开
            acodec      aac
            abitrate    128
            asample_rate 44100
            channel     2
            output      rtmp://127.0.0.1/live/[stream]_720p;
        }
        engine h264_480p {
            vfilter     {vfps=25,scale=854:-2}
            vcodec      libx264
            vbitrate    1200
            vprofile    baseline
            vpreset     veryfast
            vparams     {keyint=50:sc_threshold=0:tune=zerolatency}
            acodec      aac
            abitrate    96
            output      rtmp://127.0.0.1/live/[stream]_480p;
        }
    }
}

要点:

  • gop_cache on 让新观众一连上就拿到最近的关键帧,HTTP-FLV 秒开基本靠它。
  • keyint ≈ 2s、sc_threshold=0 禁用场景切换强制关键帧,避免 HLS 首切到非关键帧黑屏。
  • tune=zerolatency 降低编码缓冲。

3) Nginx(前置 HTTP/2/TLS 与静态 HLS)

yum install -y nginx

/etc/nginx/nginx.conf(关键段)

worker_processes auto;
events { worker_connections  65535; }

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout  65;
    client_max_body_size 10m;

    # 绑定到两张网卡各自 IP(两个 server 块)
    upstream srs_flv {
        server 127.0.0.1:8080;
    }

    server {
        listen 80;
        server_name cn.example.com;
        listen 443 ssl http2;
        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;

        # 只在 CN2 IP 上监听
        listen 203.0.113.10:80;
        listen 203.0.113.10:443 ssl http2;

        location /hls/ {
            root /opt/srs/trunk/objs/nginx/html;
            add_header Cache-Control no-cache;
        }
        location ~ \.flv$ {
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_pass http://srs_flv;
        }
    }

    server {
        listen 198.51.100.20:80;
        listen 198.51.100.20:443 ssl http2;
        server_name sea.example.com;

        location /hls/ {
            root /opt/srs/trunk/objs/nginx/html;
            add_header Cache-Control no-cache;
        }
        location ~ \.flv$ {
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_pass http://srs_flv;
        }
    }
}

 

坑 3:Nginx 代理 HTTP-FLV 要确保 proxy_http_version 1.1 与长连接,且关闭不必要的压缩,避免 chunked 断流。

调度:GeoDNS + HTTPDNS

DNS 受运营商劫持或跨网解析不准时,HTTPDNS 兜底非常关键。我们同时提供两个域名:

  • cn.example.com → 解析到 203.0.113.10(CN2)
  • sea.example.com → 解析到 198.51.100.20(Intl)

App 内优先走 HTTPDNS 接口拿到最优 IP,浏览器端走 GeoDNS。

1) 轻量 HTTPDNS(OpenResty/Lua 示例)

server {
    listen 8081;
    server_name httpdns.example.com;

    location /resolve {
        default_type application/json;
        content_by_lua_block {
            local cjson = require "cjson.safe"
            local ip = ngx.var.arg_ip or ngx.var.remote_addr
            -- 简化:根据 GeoIP 判断区域
            local country = ngx.var.geoip2_data_country_code or "ZZ"

            local resp = {}
            if country == "CN" then
                resp = { A = {"203.0.113.10"}, TTL = 30, host="cn.example.com" }
            elseif country == "SG" or country == "TH" or country == "MY" or country == "VN" or country == "ID" or country == "PH" then
                resp = { A = {"198.51.100.20"}, TTL = 30, host="sea.example.com" }
            else
                resp = { A = {"198.51.100.20"}, TTL = 30, host="sea.example.com" }
            end
            ngx.say(cjson.encode(resp))
        }
    }
}

App 侧拉取:

GET https://httpdns.example.com/resolve?host=live.example.com&ip=1.2.3.4

拿到 A 记录后直连对应域名或直连 IP。

坑 4:HTTPDNS 的证书策略要匹配主域,避免直连 IP 时 TLS 名称校验失败;可以返回域名并开启 SNI。

编码与“秒开”优化要点

GOP 与首帧

keyint≈2s(25fps → 50),sc_threshold=0,tune=zerolatency。

SRS gop_cache on,HTTP-FLV 客户端拿到关键帧立即解码,首帧稳定在 < 800ms(网络好时)。

多码率 ABR

1080p 4.5Mbps、720p 2.5Mbps、480p 1.2Mbps。

App 端 起播优先 480p,首帧出图后再平滑切到 720p/1080p,“先亮后清”。

HLS

hls_fragment=1 + hls_window=3,配合 EXT-X-PREFETCH(如需 LL-HLS 可另用),但 iOS 上仍受限于播放器策略。

WebRTC(可选)

真正亚秒级要求时启用,但服务器与带宽成本更高;本文以 HTTP-FLV 为主。

转码与上行(FFmpeg 样例)

主播端 → Ingress(RTMP)

ffmpeg -re -stream_loop -1 -i demo.mp4 \
  -c:v libx264 -preset veryfast -profile:v high -g 50 -keyint_min 50 -x264-params "scenecut=0:tune=zerolatency" \
  -c:a aac -b:a 128k -ar 44100 -f flv rtmp://cn.example.com/live/room01

服务端 ABR 转码(GPU 版本,T4 上 NVENC)

ffmpeg -y -hwaccel cuda -i "rtmp://127.0.0.1/live/room01" \
  -map 0:v -map 0:a -c:v h264_nvenc -preset p4 -b:v:0 2500k -g 50 -sc_threshold 0 -tune ll \
  -c:a aac -b:a 128k -f flv rtmp://127.0.0.1/live/room01_720p \
  -map 0:v -map 0:a -c:v h264_nvenc -preset p4 -b:v:1 1200k -g 50 -sc_threshold 0 -tune ll \
  -c:a aac -b:a 96k -f flv rtmp://127.0.0.1/live/room01_480p

访问控制与安全

# 仅允许推流入口来自主播侧网段或签名鉴权
iptables -A INPUT -p tcp --dport 1935 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 1935 -j DROP

# Nginx 限速与 CC 防护(示例)
limit_req_zone $binary_remote_addr zone=flv:10m rate=10r/s;

SRS 支持 on_connect 回调做签名鉴权(对接业务 API)。

监控与压测

  • 网络:blackbox_exporter 对 cn.example.com/live/room.flv 与 sea.example.com/... 做采样;mtr 每 1 分钟跑一次,北京/广州/新加坡各一台云探针回报 RTT、丢包、抖动。
  • 系统:node_exporter 监控 CPU/Load/IRQ、网卡队列丢包。
  • 业务:SRS 的 API /api/v1/streams/ 拉并发、码率、丢包。
  • 起播延迟测量:前端 SDK 上报 t_first_frame - t_play_click,按地域维度聚合 p50/p90/p95。

上线前自检清单

  •  rp_filter=2,策略路由命中正确(ip rule、ss -tnlp + conntrack -L 验证)。
  •  ping -M do -s 1472 到常见省份节点通过。
  •  Nginx 两个 server 仅在各自 IP 上监听。
  •  SRS gop_cache on,关键帧间隔与 HLS fragment 对齐。
  •  ABR 起播默认 480p,首帧后平滑升码。
  •  HTTPDNS 可用,App 端兜底策略生效。
  •  监控告警:秒开率、卡顿率、RTT 抖动阈值。

实测数据(上线后一周)

地区 路径 首帧 p50 首帧 p95 秒开率(≤1s) 重缓冲率
北京联通 CN2 620ms 980ms 98.6% 0.8%
上海电信 CN2 580ms 940ms 99.1% 0.7%
广州移动 CN2 650ms 1.05s 97.9% 1.0%
新加坡 Intl 430ms 820ms 99.3% 0.6%
曼谷 Intl 510ms 910ms 98.7% 0.9%

与改造前相比:平均首帧 -420ms,p95 -620ms;大促晚高峰也能稳定“点开即亮”。

常见坑与现场解法

CN2 入口好但回程跑 163

现象:RTT 抖动大,峰值卡顿。

解法:联系线路商锁回程;或在我方做 源地址策略路由 + 独立 IP 池,让回程从 CN2 表走 CN2 口(上文已配)。

HLS 黑屏 1–2s

现象:iOS 偶发首屏黑。

解法:sc_threshold=0 + keyint=fragment*fps;必要时加 -force_key_frames "expr:gte(t,n_forced*2)"。

HTTP-FLV 偶发断流

现象:长连接被某些代理拆分。

解法:确保 proxy_http_version 1.1,关闭 gzip,保持 Connection: keep-alive;客户端层面支持断点重连。

突发并发把 accept 队列顶满

解法:somaxconn、tcp_max_syn_backlog 拉满;Nginx worker_connections 到 65k;必要时做 多进程多端口 分摊。

MTU/分片导致的隐藏丢包

解法:全链路扫 ping -M do;必要时把 tun/vxlan 降 MTU 至 1450 并端到端一致。

滚动发布与回退

  • 灰度:先放 10% 解析到新节点(GeoDNS 权重),稳定后 50% → 100%。
  • 回退:DNS 权重秒级切回旧集群;Nginx 与 SRS 配置双份,systemctl reload 即回滚。
  • 数据对比:灰度期间同时上报秒开与卡顿指标,压线阈值 15 分钟观察。

上线后一周,我们迎来一位头部主播的带货首秀。开播后,我站在机柜前看着监控大盘,国内三网曲线像尺子一样平直,东南亚探针的首帧 p95 一直压在 900ms 内。弹幕里有人夸“这次点开就亮”,我把手机亮给旁边的同事看,他伸手在机柜上拍了一下,说:“这次真稳。”
那一刻我明白,所谓“秒开”,不是某个参数的魔法,也不是某条神秘线路的传说,而是架构、网络、编码、调度一次次校准后的系统性结果。你按上面的步骤把路打通、把关键点设对,它就会像今晚这样,安静地、稳定地把光送到每一个屏幕前。

一键复盘(最关键的 10 条)

  • 双口双 IP:CN2 供国内、国际口供东南亚。
  • 策略路由:基于源地址,确保“从哪进从哪出”。
  • SRS gop_cache on:HTTP-FLV 秒开的核心。
  • GOP≈2s:keyint=50@25fps,sc_threshold=0。
  • ABR 起播低码:先亮后清,减少首帧等待。
  • Nginx 分网口监听:两个 server 各自绑定 IP。
  • BBR + fq:给长连接“呼吸”的空间。
  • MTU 校验:消灭隐性丢包和分片重传。
  • GeoDNS + HTTPDNS:解析不准有兜底。
  • 监控闭环:首帧、卡顿、RTT 抖动,全部度量。

如果你也在香港搭直播,总结成一句话:线路选对,路由走顺,GOP 调好,缓存开对,解析落位——“秒开”就是水到渠成。

目录结构
全文