直播项目选型:从采集推流到分发互动的全链路拆解
做直播项目选型这件事,我踩过的坑比写过的代码还多。标题里说的“项目推荐”,我不打算给你罗列一堆名字让你去收藏夹吃灰,而是按一条真实直播链路的顺序,把直播项目从采集、推流、服务端、分发、互动到周边工具,一层层拆开讲清楚:每一层该选什么类型的项目、判断标准是什么、参数怎么算、坑在哪里。不管你是想给自己搭一个能跑起来的直播 Demo,还是公司要上一套带货/课堂/赛事直播系统,或者你只是想在简历上补一个完整的流媒体项目,下面这套思路都能直接拿去用。文中涉及具体软件的地方我用功能代称,参数和命令是通用的,你可以对着手边的工具替换,重点在于“为什么这么选”和“怎么验证选对了”。
1. 直播项目推荐的选型逻辑:先定场景,再定技术栈
1.1 先画三张图,别急着下依赖
我见过太多人一上来就找“最火的开源流媒体服务端”,装完发现业务要回看、要连麦、要鉴权,结果全部推倒重来。正确的顺序是先把三张图画出来,画不出来就别选型。
第一张是业务图:谁在看、看多久、要不要回看、要不要多人连麦、是否需要互动打赏、是否有审核要求。第二张是链路图:采集端是什么(手机、电脑、摄像头、浏览器)、上行网络条件如何、服务端在哪、分发走什么协议、播放端是网页还是 App。第三张是成本图:直播这个业务,带宽通常占到总成本的六到八成,剩下的才是计算和存储。很多项目死在第三步——技术上完全跑得通,账算不过来。
提示:把这三张图写在纸上,任何候选项目都必须能在这三张图上标注出自己负责的环节,标注不出来的,说明你对它还没理解到位。
我在实际项目里有个简单粗暴的判断法,叫“三问定架构”:同时在线人数是个位数还是五位数?能接受的延迟是 1 秒内还是 5 秒也行?需不需要事后回看?三个答案基本就把技术栈锁死了一大半。个位数并发加 5 秒延迟加要回看,一台普通云主机加切片分发就够了,别上集群;五位数并发加 1 秒延迟,那就是另一套完全不同的方案,单机自建基本不用考虑,得在分发层下功夫。
1.2 延迟、并发、成本这个三角,必须明确放弃一个
直播选型最核心的矛盾就是这个三角。低延迟意味着更小的缓冲、更多的重传、更高的带宽成本;高并发意味着要分发、要边缘节点、要更多副本;低成本意味着压缩、降码率、少转码。三者不可能同时拿满分,你一定要主动放弃一个,而不是等系统崩了被动放弃。
下面这张表是我这些年总结的对应关系,遇到场景先对号入座:
| 场景类型 | 可接受延迟 | 推荐传输方式 | 典型并发 | 成本主要开销 |
|---|---|---|---|---|
| 内部培训、录播回看 | 5 到 30 秒 | 切片分发 | 几十到几百 | 存储与回源 |
| 电商带货、秀场互动 | 1 到 3 秒 | 低延迟流式分发 | 上千 | 分发带宽 |
| 在线课堂、连麦答疑 | 200 到 800 毫秒 | 实时传输通道 | 几十到几百 | 计算与带宽 |
| 赛事、大型发布会 | 3 到 8 秒 | 切片分发加多档位 | 上万 | 分发带宽与转码 |
选型时我一般建议从“稍微多一点的延迟”开始,先让业务跑起来,再针对具体环节优化。因为延迟从 5 秒压到 1 秒,工程量可能是几倍,而业务方往往在真正体验之后,发现 3 秒也能接受。反过来,先把延迟压到极限再往回加,中间那些为极致低延迟写的补丁代码,反而成了维护负担。
1.3 我把候选项目分成四类,每类只看三个指标
为了让选型不混乱,我把直播相关项目统一分成四类:采集与编码类、服务端类、播放端类、周边工具类。每类只关注三个核心指标,其他都是加分项。
采集与编码类看:支持的输入源数量、编码参数可控程度、断流重连能力。服务端类看:支持的接入协议、能否按需转码、有没有成熟的鉴权和回调机制。播放端类看:首帧时间、弱网策略、对多种封装格式的兼容性。周边工具类看:能不能异步跑、失败能否重试、有没有可观测的输出。
这么分类的好处是,你不会拿服务端的指标去评价一个弹幕项目。我见过的典型错误是用“并发性能”去挑弹幕系统——弹幕的瓶颈从来不在消息吞吐,而在消息顺序、去重和前端渲染,几万条每秒的吞吐对现代消息中间件来说根本不是事,真正难的是让每条弹幕出现在它该出现的那一帧左右。
注意:任何“推荐清单”都有时效性,但分类方法和判断指标是稳定的。你要记的是指标,不是名字。
2. 推流采集与编码:从摄像头到第一帧数据
2.1 传输协议怎么选,一张表说清楚
协议选型是整个链路里最容易出错的地方,因为很多人把“推流协议”和“播放协议”混为一谈。这两端完全可以不一样,中间由服务端做转换。常见的几类传输方式,我按特性列一下:
| 传输方式 | 延迟量级 | 优点 | 主要短板 | 适用环节 |
|---|---|---|---|---|
| RTMP | 2 到 5 秒 | 生态成熟,推流端支持广 | 基于长连接,移动端弱网表现一般,浏览器端逐步不支持 | 推流为主 |
| SRT | 1 到 3 秒 | 抗丢包强,适合不稳定上行 | 需要两端都支持,浏览器生态弱 | 推流,尤其是远程/移动上行 |
| 基于 HTTP 的实时流 | 2 到 4 秒 | 浏览器兼容好,穿透性好 | 依赖分片或长连接保持 | 播放为主 |
| 切片分发 | 5 到 30 秒 | 兼容性最好,可缓存,成本最低 | 延迟高 | 播放、回看 |
| 实时传输通道 | 200 到 800 毫秒 | 延迟最低,天然支持连麦 | 实现复杂,服务端成本高 | 连麦、互动课堂 |
实操建议很明确:推流端优先选成熟度高、抗弱网强的方案;播放端优先选兼容性好的方案;中间的转换交给服务端。我做过一个移动端户外直播,上行是 4G,一开始用常规推流协议,主播走动就花屏,换成抗丢包更强的传输方式后,同样的网络条件下卡顿率降了一半多。这不是协议优劣的问题,是场景匹配的问题。
2.2 编码参数不是玄学,是可以算出来的
编码参数直接影响成本和质量,我把常用的计算方式和经验值都列出来,你可以直接套。
码率估算:有个粗略的工程公式,码率(Mbps) ≈ 宽 × 高 × 帧率 × 运动系数 × 0.00007。比如 1920×1080、30 帧、运动系数取 0.07(普通口播类),算下来是 1920×1080×30×0.07×0.00007 ≈ 3.05 Mbps。如果是游戏或体育画面,运动系数取 0.1 到 0.15,码率就要上到 4 到 6 Mbps。这个公式不精确,但用来做容量预估足够。
下面是分辨率与码率的经验对照,可以直接抄:
| 分辨率 | 帧率 | 视频码率 | 音频码率 | 单路小时存储 |
|---|---|---|---|---|
| 640×360 | 25 | 600 kbps | 64 kbps | 约 0.3 GB |
| 1280×720 | 30 | 1500 kbps | 96 kbps | 约 0.7 GB |
| 1920×1080 | 30 | 3000 kbps | 128 kbps | 约 1.4 GB |
| 1920×1080 | 60 | 5000 kbps | 128 kbps | 约 2.3 GB |
| 2560×1440 | 30 | 6000 kbps | 128 kbps | 约 2.7 GB |
存储那列怎么来的?单路小时存储(GB) = 码率(Mbps) × 3600 ÷ 8 ÷ 1024。以 3000 kbps 为例:3 × 3600 ÷ 8 = 1350 MB ≈ 1.32 GB,加上 128 kbps 音频约 0.055 GB,合计约 1.4 GB。这个数在你规划录制和回看容量时天天要用,建议直接记住 1080p 半小时约 0.7 GB。
关键帧间隔(GOP):GOP 帧数 = 帧率 × GOP 时长。30 帧下想要 2 秒一个关键帧,就是 60 帧。GOP 太大,首帧慢、切片不准、拖动回看定位粗;GOP 太小,码率飙升、画质下降。我一般把 GOP 定在 1 到 4 秒,且必须固定,绝不允许编码器自动插入关键帧。
缓冲设置:bufsize ≈ 2 × maxrate。这是给编码器留的“水库”,太小会因为瞬时码率超过上限而爆码,太大则码率控制反应迟钝。
2.3 实操:用命令行把链路先跑通
在碰任何服务端之前,先在本机把“编码加推流”跑通,这是最快验证参数的方式。命令里用[encoder]代指你手边的命令行编解码工具,参数是通用的:
# 本地文件循环推送,固定 2 秒 GOP,码率上限 2500 kbps [encoder] -re -stream_loop -1 -i demo.mp4 \ -c:v libx264 -preset veryfast -profile:v main -pix_fmt yuv420p \ -b:v 2500k -maxrate 2500k -bufsize 5000k \ -g 50 -keyint_min 50 -sc_threshold 0 \ -c:a aac -b:a 128k -ar 44100 -ac 2 \ -f flv rtmp://127.0.0.1:1935/live/test这里每个参数都不是随手写的。-re让推流按实时速度走,否则文件会瞬间推完;-stream_loop -1循环播放,方便长时间压测;-pix_fmt yuv420p是兼容性兜底,不少浏览器和播放器只认这个像素格式;-g 50 -keyint_min 50 -sc_threshold 0三者配合才能强制固定关键帧间隔,少了-sc_threshold 0,编码器依然会在场景切换处插关键帧,导致多档位切换时花屏;-bufsize 5000k是 maxrate 的两倍。
推出去之后怎么验证参数对不对?在服务端侧用探测工具看关键帧间隔:
# 拉一路流,打印每个关键帧的时间戳 [probe] -v error -select_streams v:0 -show_frames \ -show_entries frame=pkt_pts_time,key_frame -of csv \ rtmp://127.0.0.1:1935/live/test | grep ",1$" | head -20如果输出的时间戳间隔稳定在 2 秒左右,说明 GOP 设置生效了;如果忽长忽短,回去检查-sc_threshold。
实操心得:永远不要在生产环境第一次调参数。我习惯先用一段 5 分钟的本地素材跑满 30 分钟,观察码率曲线是否有尖峰,再看关键帧间隔是否稳定,最后才接入真实采集设备。
3. 流媒体服务端:自建与托管的关键差异在哪
3.1 服务端能力清单,少一项都要改架构
选服务端项目时,别看功能列表有多长,看这九项你实际需不需要:
| 能力 | 说明 | 是否需要提前确认 |
|---|---|---|
| 多协议接入 | 能否同时接收多种推流方式 | 强需要,决定采集端自由度 |
| 转封装 | 推流进来能否直接转成播放格式 | 强需要,决定是否需要额外组件 |
| 实时转码 | 能否输出多档位 | 按需,成本大头 |
| 鉴权 | 推流密钥、播放令牌、防盗链 | 强需要,涉及安全 |
| 录制 | 是否支持分片录制、断流续录 | 按需,影响存储方案 |
| 时移与回看 | 是否支持拖动到历史位置 | 按需,课堂和赛事常用 |
| 截图与封面 | 定时截图做封面或审核 | 按需,审核场景常用 |
| 事件回调 | 推流开始、结束、断开是否通知业务 | 强需要,业务联动全靠它 |
| 水平扩展 | 多实例如何协同 | 需提前设计,后补很难 |
这张表里我特别想强调“事件回调”。很多开源服务端默认是“哑”的,推流来了就转,走了就停,业务系统完全不知道发生了什么事。你要做到“用户开播后三秒内在列表页显示直播中”,就必须有回调机制,否则只能靠轮询,又慢又浪费。这一条如果候选项目不支持,我基本会直接放弃。
3.2 单机跑通:接收、转码、分发三段式
一套最小可用的自建服务端,我建议拆成三个容器:接收端、转码端、分发端。这样做的好处是每一段都能独立替换和压测。下面是编排文件的核心结构:
services: ingest: image: <流媒体服务端镜像> ports: - "1935:1935" - "8080:8080" volumes: - ./hls:/data/hls - ./conf/ingest.conf:/etc/ingest.conf restart: always transcoder: image: <转码工作节点镜像> environment: - SOURCE=rtmp://ingest:1935/live/test - PRESET=veryfast - GOP=50 depends_on: - ingest web: image: <静态分发镜像> ports: - "80:80" volumes: - ./hls:/usr/share/nginx/html/hls:ro - ./conf/web.conf:/etc/nginx/conf.d/default.conf:ro接收端的配置关键是切片长度和播放列表保留数量。切片 4 秒、保留 5 片,是兼容性和延迟的折中:
application live { live on; hls on; hls_path /data/hls; hls_fragment 4s; hls_playlist_length 20s; hls_cleanup on; record all; record_path /data/record; record_unique on; }hls_fragment 4s意味着播放端至少要缓冲 3 个分片才能稳定播放,也就是 12 秒左右的理论延迟,实测在 8 到 15 秒之间波动。如果你要 3 秒以内的延迟,这条路走不通,得换低延迟分发方式,这就是前面说的“三角取舍”在配置层面的体现。hls_playlist_length 20s决定了播放器能往回拖多远,太短用户拖不了,太长占内存,20 秒是个常见起点。
分发端的反向代理要记得打开跨域和正确的 MIME 类型,否则播放器会报“格式不支持”:
location /hls/ { add_header Access-Control-Allow-Origin *; add_header Cache-Control "no-cache"; types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /usr/share/nginx/html; }注意:切片文件绝对不能设长缓存。播放列表每次请求都应该是新的,否则用户永远停留在 20 秒前的画面,我上次遇到这个问题查了整整一个下午。
3.3 容量估算:先把账算明白再谈架构
这部分是我认为最应该被写进每个团队文档的内容,但恰恰最少人做。估算分三步。
第一步算出口带宽:出口带宽 = 并发观看人数 × 平均码率 × 冗余系数。以 1000 人、平均 2.5 Mbps、冗余 1.2 计算,是 1000 × 2.5 × 1.2 = 3000 Mbps = 3 Gbps。这个数字意味着单机自建基本没戏,必须走分发网络。如果只有 50 人,50 × 2.5 × 1.2 = 150 Mbps,一台不错的云主机就能扛,自建完全合理。这就是“多少人看”这第一问的价值。
第二步算转码算力:转码是 CPU 密集型,普通云主机用软件编码,1080p30 大概一路吃 2 到 4 个核。你要同时输出 720p 和 360p 两档,那就是额外两路编码,8 个核起步,还得留余量。有没有硬件编码单元,成本差三到五倍,这是选型时一定要确认的。
第三步算存储:录制一天按 8 小时算,1080p 单路约 11 GB,100 个主播一天就是 1.1 TB。这里的关键不是算出来多大,而是定好保留策略:录制保留 7 天还是 30 天?切片保留多少?我见过一个项目上线三个月才发现存储账单是预算的六倍,原因是录制默认永久保留,谁都没去关。
实操心得:容量估算要按“峰值”算,按“均值”算出来的方案上线必炸。我一般取日均峰值的 2 倍作为规划容量,留出活动和大促的余量。
4. 互动层项目:弹幕、在线人数与打赏账务
4.1 弹幕系统的三个真坑:顺序、限流、存储
弹幕看着简单,实际上是我见过最容易翻车的模块。第一个坑是顺序。用户 A 的弹幕和用户 B 的弹幕几乎同时到达,服务端多线程处理,如果直接按处理完成时间广播,同一秒内的弹幕顺序会乱跳,观众看到的就是“答非所问”。解决办法是给每条消息打一个单调递增的序号,服务端广播时按序号排序,客户端渲染时按序号插入缓冲队列。
第二个坑是限流。不做限流的弹幕系统,被刷一次就废了。我一般用令牌桶,每个用户每秒 1 条、每分钟 10 条,同一内容 5 秒内重复直接丢弃。限流要在网关层做,不要等业务层再判断,否则带宽和 CPU 已经被无效消息吃掉了。
第三个坑是存储模型。弹幕的读取特征是“最近十分钟的读取频率极高,历史几乎无人问津”。所以正确的做法是分层:热数据放在内存的有界队列里,只保留最近 N 条,冷数据异步落库并按天分区。把所有弹幕都放数据库然后每次查询都ORDER BY,十万条就能把接口拖到超时。
长连接层的连接管理也有讲究。心跳间隔我一般设 25 秒,服务端 60 秒无心跳就断开;重连必须带退避,第一次 1 秒、第二次 2 秒、依次翻倍到 30 秒封顶,否则用户网络抖一下,几千个客户端同时重连,服务端直接被打挂。
4.2 在线人数:别用总数,用去重后的活跃数
“直播间显示 12345 人在线”,这个数字的真实性是一回事,技术上怎么算准确是另一回事。最常见的错误是把连接数当人数——一个人开了手机和电脑两个设备,就被算成两个。
我的做法是用一个带过期时间的集合结构:用户进入时写入用户标识,设置 90 秒过期,每次心跳续期;查询时统计集合内元素数量。这样天然的解决了“掉线自动剔除”和“多设备去重”。如果人数规模大到统计集合本身成为负担,就改成按分钟分桶的概率统计结构,误差控制在 2% 以内,业务上完全够用。
代码大致长这样,关键是 TTL 续期和去重的配合:
// 用户心跳:写入并续期 async function heartbeat(roomId, userId) { const key = `live:online:${roomId}`; await redis.zadd(key, Date.now(), String(userId)); await redis.expire(key, 90); } // 统计:清掉过期时间戳后计数 async function countOnline(roomId) { const key = `live:online:${roomId}`; const cutoff = Date.now() - 90000; await redis.zremrangebyscore(key, 0, cutoff); return await redis.zcard(key); }用有序集合的分数存时间戳,既完成了去重(同一个用户标识只占一个成员),又完成了过期清理,比单纯用过期时间更可控。统计接口本身要做缓存,5 秒到 10 秒更新一次足够,没人会盯着数字跳。
4.3 打赏与礼物:幂等和流水是底线
互动玩法里最不能出问题的是账务。这一块我只强调三件事,都是踩过坑换来的。
第一,所有写操作必须有幂等键。客户端点击“送出礼物”,可能因为网络超时重试三次,服务端必须靠一个全局唯一的请求标识去重。没有幂等键的送礼接口,早晚会出现一个用户送了一次扣了三次钱的事故。
第二,余额扣减和流水记录必须在一个事务里。先扣余额再写流水,中间挂掉,钱没了账对不上;先写流水再扣余额,中间挂掉,白送礼物。正确做法是用数据库事务或者带条件的原子更新,语句形如UPDATE account SET balance = balance - ? WHERE user_id = ? AND balance >= ?,靠影响行数判断是否成功,再写流水。
第三,必须有每日对账。业务流水、账户余额、支付渠道流水三方对账,差异超过阈值告警。对账不是财务的事,是研发的事,我一般要求在上线第一天就把对账脚本写好,因为问题往往在最开始就埋下了。
注意:互动玩法的技术难点从来不在“送出去”,而在“记录对得上”。凡是涉及钱的模块,宁可慢一点、多一次校验,也不要为了性能走捷径。
5. 直播周边项目:录制切片、字幕与监控
5.1 自动切片与精彩集锦:关键帧是硬约束
直播切片这个需求很普遍——游戏高光、课程重点、带货爆品片段。技术上分三步:定位、切割、转封装。
定位有几种思路。基于音频能量最简单,计算每秒的音量均方根,取超过阈值的区间;基于画面变化更准,用场景切换检测,画面剧烈变化处大概率是精彩点;最靠谱的是人工标记加热度信号(比如弹幕密度、打赏峰值)自动加权。我实际做过的效果最好的是“弹幕密度峰值为主,画面变化为辅”,因为观众的情绪反应通常比画面本身更准。
切割有个硬约束:必须从关键帧切开。如果从非关键帧切,播放器需要向前解码到最近的关键帧才能显示,开头会黑一下或者花屏。前面章节为什么反复强调固定 GOP,到这里就用上了——GOP 固定 2 秒,切片的起始点误差最多 2 秒,完全可以接受。
# 从第 300 秒开始,切 30 秒,重新编码保证首帧可播 [encoder] -ss 300 -i input.mp4 -t 30 \ -c:v libx264 -preset veryfast -g 50 -keyint_min 50 \ -c:a aac -b:a 128k -avoid_negative_ts make_zero \ output_clip.mp4-avoid_negative_ts make_zero这个参数很关键,不加的话切出来的文件时间戳可能是负数,部分播放器直接拒绝播放。切完之后一定要用探测工具验证一下时长和首帧,别相信命令没报错就是成功。
5.2 字幕与转写流水线:异步和分片是核心
直播字幕是个典型的异步任务,绝对不能放在推流主链路上。我的做法是把直播的音频按 10 秒一段切出来,丢进队列,转写结果带着时间戳回来,前端按时间轴渲染。
为什么是 10 秒?因为太短了调用次数多、成本高、上下文不够;太长了延迟大、断句断得莫名其妙。10 到 15 秒是实践中比较舒服的区间,最后再把相邻分片的重叠部分做一次拼接去重。
准确性上有几个提升技巧:一是维护一份领域热词表,把行业专有名词、商品名、人名加进去,识别准确率能提升一大截;二是把上一片的结果作为下一片的上下文提示;三是对数字、时间、金额这类关键信息做后处理规则校正。
字幕的显示也要注意:一行不要超过 16 个字,超过就换行;每条至少停留 1.5 秒,否则闪一下就没了;标签要和语音对齐,误差控制在 0.5 秒内,否则观众会觉得“嘴型和字幕对不上”。
5.3 监控指标:只看带宽的团队早晚出事
直播监控和普通 Web 监控完全不一样,下面这组指标是我认为必备的:
| 指标 | 含义 | 健康阈值参考 | 异常时的第一反应 |
|---|---|---|---|
| 推流成功率 | 开始推流到稳定出流的比例 | 大于 99% | 查采集端网络和鉴权 |
| 首帧时间 | 点击播放到出画面 | 小于 2 秒 | 查切片长度和回源 |
| 卡顿率 | 播放中卡顿时长占比 | 小于 3% | 查上行丢包和分发节点 |
| 断流率 | 推流中途断开比例 | 小于 1% | 查网络抖动和编码器 |
| 转码失败率 | 转码任务失败比例 | 小于 0.5% | 查算力和源流参数 |
| 播放失败率 | 返回错误的比例 | 小于 1% | 查鉴权、跨域、格式 |
这些指标要能按“主播”“地区”“运营商”“设备型号”四个维度下钻。我遇到过一个问题,整体卡顿率只有 2%,看起来非常健康,但按运营商下钻发现某一家的卡顿率是 18%,原因是那条链路的回源走了跨网。只看大盘指标,这种问题永远发现不了。
实操心得:监控面板不要超过一屏,一屏放不下的指标说明你还没想清楚哪个重要。我通常只把“首帧时间、卡顿率、断流率、错误率”四个放在最上层,其他全部下钻。
6. 常见问题与排查实录
6.1 推流成功但播放黑屏
这是最高频的问题,我自己遇到过不下十次。按下面的顺序查,基本能定位:
第一,看编码格式。部分浏览器和播放器对更新的编码标准支持不全,把视频编码改成通用性最好的那种,像素格式必须是yuv420p,这个前面命令里写过。
第二,看封装格式和时间戳。用探测工具看一下流里的时间戳是否连续,如果有跳变或者从负数开始,播放器会直接放弃。
第三,看切片有没有真的生成。进容器里ls一下切片目录,如果只有播放列表没有分片,说明切片参数有问题或者磁盘满了。
第四,看跨域。浏览器控制台报跨域错误却没人注意,是最冤的一种。播放列表和切片都要允许跨域,而且Access-Control-Allow-Origin不能只配在播放列表上。
第五,看鉴权。播放地址带了错误的令牌,服务端返回 403,播放器只会显示黑屏,不会告诉你原因。直接用curl -I请求播放地址看状态码,比在播放器里猜快得多。
6.2 卡顿排查速查表
卡顿的原因太多,我整理成一张表,按“先外后内”的顺序排查:
| 现象 | 可能原因 | 验证方式 | 处理 |
|---|---|---|---|
| 所有观众都卡 | 源流码率过高或上行不足 | 看推流端码率曲线 | 降码率或换抗丢包方案 |
| 部分观众卡 | 分发节点或地域链路 | 按地区维度看卡顿率 | 调整分发策略 |
| 固定时间点卡 | 关键帧间隔过大 | 探测关键帧时间戳 | 固定 GOP,重新推流 |
| 每隔几秒卡一下 | 切片长度或播放列表刷新 | 看切片时长 | 缩短切片或调整缓冲 |
| 声音正常画面卡 | 视频解码能力不足 | 看客户端设备 | 降分辨率或换档位 |
| 越看越卡 | 播放器缓冲未清理 | 长时间挂机复现 | 换播放器或限制缓冲 |
我印象最深的一次是“每隔 6 秒卡一下”,查了两天,最后发现是播放列表里保留的分片数正好是 3 个,而切片是 2 秒,播放器每次都要等新分片刷新,刚好卡在临界点上。把保留数加到 5 个就彻底好了。这种问题在文档里永远找不到,只能靠对参数的敏感度。
6.3 成本失控的三个隐性原因
技术上跑得好好的项目,最后死于账单,这很常见。三个最隐蔽的原因:
一是转码档位叠加。本来只想要 720p,配置里默认开了四档,每档一路 CPU,成本直接四倍。上线前一定要明确“我到底需要几档”,而不是全部打开。
二是录制永不清理。默认配置往往是永久保留,三个月后存储费用超过带宽。必须在第一天就定好保留策略并写成定时任务。
三是分发没有命中缓存。切片文件的缓存命中率如果只有 50%,回源带宽就是正常值的两倍。检查缓存键是否包含多余的参数、切片是否设置了正确的缓存头,这两个小地方经常吃掉一大块预算。
注意:每月做一次“成本归因”,把账单按推流、转码、存储、分发四块拆开,哪块环比涨了 30% 以上就立刻查。等到季度末才发现,追溯成本极高。
7. 我个人在直播项目上的一些体会
做了这么多套直播系统,我最大的体会是:选型不是选最强的,是选最匹配的,而匹配的前提是你把业务想清楚了。前面那些协议对比、参数计算、容量估算,本质上都是在把模糊的需求变成可计算的数字。数字一旦清晰,选什么项目其实不用纠结,符合条件的那几个都能用。
第二个体会是先在本地跑通一条最短链路。我现在做任何直播相关的东西,第一步永远是本机推一个文件流、拉一个播放器看画面,把编码参数、切片、跨域这些基础问题在半小时内暴露完。基础设施跑不通,谈架构都是空中楼阁。
第三个体会是把参数写成注释。-g 50和hls_fragment 4s这种数字,三个月后你自己都不记得为什么这么定。我现在习惯在配置文件旁边写一行注释说明推导过程和当时的压测结论,这个习惯帮我省过很多次返工。
最后一个小技巧分享给刚开始接触这块的朋友:不要一上来就追求低延迟和高并发,先用最低成本的方式把“能看、能听、能互动”跑起来,然后拿真实数据去决定优化哪一环。直播这个东西,纸面上算不出来的问题,跑起来十分钟全出来了。