00 · 设计推演:从零造一个直播系统
“为什么直播延迟总是 3-10 秒?不是做不到实时——是百万并发和实时,你只能选一个。“
开场:先算带宽
老陈:需求:一个主播,100 万观众,720p 画质。先把账算清楚。
小林:720p 大概 1.5 Mbps?
老陈:对。那主播要上传多少数据?
小林:……要看架构?
老陈:先假设最简单的:主播直接发给每个观众。
小林:100 万 × 1.5 Mbps = 1.5 Tbps。不可能。
老陈:家庭宽带上行通常只有 30-100 Mbps。差了五万倍。
这就是直播系统所有设计的起点:主播上行只有一条流,观众有一百万条。
v0:主播直连(Mesh)
主播 ──► 观众1
──► 观众2
──► ...
──► 观众100万
主播上行 = 100万 × 1.5 Mbps = 1.5 Tbps ★ 不可能
失败:
| # | 问题 | 数据 |
|---|---|---|
| 1 | 主播上行带宽 | 需要 1.5 Tbps,实际只有 50 Mbps |
| 2 | NAT 穿透 | 大部分观众在 NAT 后面 |
问题 2 其实有解(WebRTC 的 STUN/TURN),但问题 1 是物理限制,绕不过去。
老陈:注意 v0 虽然荒谬,但它暴露了核心矛盾:
主播只有一条上行,观众有一百万条下行。
后续所有架构,本质上都是在解决”如何把 1 变成 100 万”。
v1:中央服务器转发
改动:主播只推一路到服务器,服务器复制给所有观众。
主播 ──► 服务器 ──► 观众1
──► 观众2
──► ...
主播上行: 1.5 Mbps ✓
服务器下行: 100万 × 1.5 Mbps = 1.5 Tbps ★
失败:
单台服务器出口带宽通常 10-40 Gbps
需要 1.5 Tbps → 需要几十上百台服务器
但至少思路对了:主播上行解决了。剩下的是服务器集群的问题。
v2:服务器集群
改动:加机器。
┌─► 边缘服务器1 ──► 2万观众
主播 ──► 中心 ─────┼─► 边缘服务器2 ──► 2万观众
└─► ...(50 台)
中心出口: 50 × 1.5 Mbps = 75 Mbps ✓
边缘总出口: 1.5 Tbps(分摊到 50 台,每台 30 Gbps)
改进:能撑住了。
失败:
| # | 问题 | 说明 |
|---|---|---|
| 1 | 跨地域延迟 | 观众在北京,服务器在广州 → RTT 50ms+ |
| 2 | 单点故障 | 中心挂了全挂 |
| 3 | 带宽成本高 | 数据中心带宽比 CDN 贵得多 |
| 4 | 扩展困难 | 观众涨到 500 万要重新规划 |
v3:CDN 分发
改动:把分发交给 CDN(内容分发网络)。
主播 ──► 源站 ──► CDN 中心节点 ──► 省级节点 ──► 边缘节点(运营商机房)──► 观众
★ 边缘节点离用户最近(同城甚至同运营商)
CDN 的核心价值:缓存复用
没有 CDN: 100 万观众 = 100 万条独立流
有 CDN: 边缘节点缓存一份,同节点的 N 个观众共用一条流
→ 回源流量只有"边缘节点数 × 码率"
★ 假设 1000 个边缘节点:
回源流量 = 1000 × 1.5 Mbps = 1.5 Gbps (而不是 1.5 Tbps)
★ 降低 1000 倍
改进:
| 改进 | 说明 |
|---|---|
| 延迟低 | 边缘节点离用户近 |
| 成本低 | CDN 带宽比 IDC 便宜 |
| 扩展容易 | CDN 厂商有海量节点 |
| 抗突发 | CDN 天然能吸收流量尖峰 |
★ 但 CDN 的复用机制有个前提:内容必须是”完整的数据块”。
CDN 的工作方式:
边缘节点缓存一个"对象"(文件/切片)
多个用户请求同一个对象 → 复用
★ 如果你来一个字节就转发一个字节,就没法形成可缓存的对象
这就是延迟的来源。
v4:延迟从哪来
为了适配 CDN,必须用”切片”(HLS):
服务端把流切成小片段(每个 6-10 秒)
segment0.ts, segment1.ts, ...
生成播放列表 m3u8
客户端定时拉 m3u8,下载最新的 ts 播放
延迟构成:
① 切片等待: 必须等一个切片完整生成
6 秒切片 = 至少 6 秒延迟
② 播放列表刷新: 客户端每 3-10 秒拉一次 m3u8
③ 客户端缓冲: 为了抗抖动,缓存 2-3 个切片(12-30 秒)
④ 传输: 几百毫秒
总计: 默认配置下 20-40 秒,优化后 5-20 秒
★ 关键洞察:缓冲是延迟的主要来源,但缓冲是必要的。
为什么不减小缓冲?
网络是抖动的:
· 带宽忽高忽低
· 偶尔丢包、重传
· 切换 WiFi / 4G
缓冲的作用: 用"提前下载"吸收"网络抖动"
· 缓冲 30 秒 → 能扛住 30 秒的网络中断
· 缓冲 0.5 秒 → 网络卡 0.5 秒就卡住了
★ 这就是核心权衡: 延迟 vs 流畅度
直播: 流畅优先(缓冲大,延迟高)
视频会议: 延迟优先(缓冲小,允许偶尔卡)
老陈:这是全书反复出现的那个权衡的又一次出现:
“用一致性换可用性”、“用写放大换读放大”、“用延迟换流畅度”。
当你看到一对矛盾时,先问:“我的场景更需要哪一边?” 答案清楚了,参数是唯一确定的。
v5:降低延迟的三条路
如果业务确实需要低延迟(直播带货、在线教育、体育赛事),有三条路:
路 A:优化 HLS(LL-HLS)
改动:
① 部分切片(Partial Segment): 200-500ms
② 阻塞式播放列表刷新: 服务端 hold 住请求,有新内容立即返回
③ 预加载提示
效果: 延迟从 5-20 秒降到 2-5 秒
代价: 服务端复杂度上升;CDN 支持不好(很多 CDN 不支持 LL-HLS)
路 B:换协议(HTTP-FLV)
改动: 不切片,流式传输
服务端收到一个 tag(帧)就立即发给客户端
效果: 延迟 1-3 秒
代价: 需要专门的播放器(flv.js);iOS Safari 不支持
★ CDN 可以部分复用,但不如 HLS
路 C:换传输层(WebRTC)
改动: UDP + RTP/RTCP
帧级别传输,无切片
有拥塞控制、FEC、NACK
效果: 延迟 50-500ms
代价: ★★ CDN 无法复用!每个观众一条独立流
带宽成本上升 5-10 倍
需要 SFU 集群(而不是 CDN)
★ 三条路的本质差异:
| LL-HLS | HTTP-FLV | WebRTC | |
|---|---|---|---|
| 延迟 | 2-5s | 1-3s | <500ms |
| CDN 复用 | ★★★★ | ★★★ | ★ 无 |
| 成本 | 低 | 中 | 高 |
| 兼容性 | ★★★★★ | ★★★ | ★★★★ |
| 扩展 100 万 | ★★★★★ | ★★★★ | ★★ |
老陈:这里有个绕不开的矛盾,值得单独说:
CDN 的缓存复用要求”数据块化”,而低延迟要求”立即发送”。
这是本质冲突,没有完美解法。
所以选型问题最终变成:你的业务能接受什么延迟?
5-10 秒可接受 → HLS + CDN(成本最低,最稳) 1-3 秒 → HTTP-FLV(性价比高) <1 秒 → WebRTC(成本高 5-10 倍,需要互动才值)
v6:完整的现代直播架构
实际生产系统通常是多种协议并存:
主播 ──RTMP/WebRTC──► 接入层(边缘节点)
│
▼
中心(转码/录制/审核)
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
HLS 分发 HTTP-FLV 分发 WebRTC SFU
(iOS/默认) (PC/Android) (连麦/超低延迟)
★ 为什么推流用 RTMP 而不用 WebRTC?
推流(上行)和拉流(下行)的需求不同:
上行: 只有一路,对延迟不敏感(1-3 秒可接受)
→ RTMP 成熟、稳定、CDN 支持好
下行: 百万路,场景多样
→ 按客户端能力/需求选不同协议
★ 这是"按链路方向分别优化"的思路
上行的瓶颈是"主播带宽",下行的瓶颈是"CDN 复用"
两者约束不同,解法就该不同
★ 这一节真正的收获
需求: 1 个主播 → 100 万观众,720p
│
├─ v0 主播直连
│ 失败: 需要 1.5 Tbps 上行(实际 50 Mbps,差 5 万倍)
↓
├─ v1 中央服务器转发 ← 主播上行解决(1.5 Mbps ✓)
│ 失败: 服务器出口 1.5 Tbps,单机扛不住
↓
├─ v2 服务器集群
│ 失败: 跨地域延迟;带宽成本高;扩展困难
↓
├─ v3 CDN 分发 ← ★ 缓存复用,回源流量降 1000 倍
│ 代价: 需要"数据块化" → 切片 → ★ 延迟 5-20 秒
↓
├─ v4 分析延迟来源
│ ★ 核心权衡: 缓冲越大越流畅,但延迟越高
↓
└─ v5 降延迟三条路
LL-HLS(2-5s) / HTTP-FLV(1-3s) / WebRTC(<500ms)
★ 延迟 vs CDN复用 vs 成本,三者不可兼得
五个可以迁移的思维模式
① 先算物理量的账
1.5 Tbps vs 50 Mbps,差 5 万倍——这个数字直接判了 v0 死刑,也指明了后续方向。 在架构设计前,先把带宽、QPS、数据量算出来。很多方案在算术阶段就被淘汰了。
② 复用是把 N 变成 1 的关键
CDN 的核心不是”更多的机器”,而是”缓存复用”。 同样的思路:连接池、线程池、Buffer Pool、CDN——都是”用共享代替独占”。
③ 每个优化都有前提
CDN 复用的前提是”内容可缓存”,也就是”数据块化”。 当你用一个机制时,问清楚它的前提条件是什么,代价是什么。
④ 矛盾不可消除,只能按场景选边
延迟 vs 流畅度、延迟 vs CDN 复用 vs 成本。 先明确业务优先级,再选方案。没有”都想要”的选项。
⑤ 按链路方向分别优化
上行(1 条)和下行(100 万条)约束完全不同,解法就该不同。 先分解问题,再分别优化。这是第 2 章”分治”思想在架构层面的应用。
现在,轮到你了
练习:我们已经解决了”分发”。还差两个关键问题:
问题 A:主播网络不好(比如 4G 信号弱,带宽从 2 Mbps 掉到 300 Kbps),画面质量怎么办?
问题 B:观众网络不好,怎么保证他不卡?
提示(不要用,先自己想):
- A 和 B 的解法一样吗?(★ 关键区别在哪?)
- 主播端能做什么?(想想”自适应码率”——但推流是单向的,谁来决定码率?)
- 观众端能做什么?(多码率?动态切换?切换时要不要重新缓冲?)
- HLS 的 m3u8 里为什么能写多个不同码率的播放列表?
- WebRTC 的 GCC 拥塞控制是怎么做的?它跟 HLS 的自适应码率有什么本质区别?
老陈:答案的关键区别:
- 观众端自适应(ABR):服务端预先转码出多档码率,客户端根据自身带宽选。 ★ 这是 HLS/DASH 的做法,成熟但需要服务端转码(成本高)
- 主播端自适应:推流侧根据上行带宽动态调整编码码率。 ★ 只能改主播自己的,观众都得跟着降级
- WebRTC 的做法:拥塞控制 + 动态码率 + 可以为每个观众单独调整(SFU 转发不同层)
这里有个有意思的点: HLS 是”服务端转 N 份,客户端选”;WebRTC 是”实时测量,实时调整”。 前者简单但浪费转码资源,后者精确但复杂度高。
又是一对权衡。
下一节
这一节我们用直播系统练了”从约束推导架构”,下一节把这些经验提炼成可复用的决策框架——特别是那个最关键的问题:什么时候该自己造?
带着这个问题去读:如果你要做直播,你会自己写 CDN 吗?为什么?