1. 为什么是 Nginx-RTMP 而不是 FFmpeg 或其他方案?
在 Ubuntu 22.04 上搭一个能扛住几十路并发推流、同时支持 Web 端低延迟播放的视频服务器,很多人第一反应是“直接用 FFmpeg 转发不就完了?”——我试过,也踩过坑。去年给一家本地教育机构做直播系统时,最初就是用ffmpeg -i rtmp://localhost:1935/live/src -c copy -f flv rtmp://localhost:1935/live/out这种链式转发跑起来的。表面看能用,但第三天就崩了:学生端卡顿率飙升到 40%,后台日志里全是Connection refused和Broken pipe;重启 FFmpeg 后不到两小时又挂;更麻烦的是,一旦要加 HLS 切片、录制回放、多分辨率自适应,就得堆砌七八个 FFmpeg 实例,CPU 占用直冲 98%,htop里密密麻麻全是ffmpeg进程,根本没法监控和限流。
后来我们彻底重做,选了 Nginx-RTMP 模块。不是因为它“新”,而是它把流媒体服务里最硬的几块骨头——连接管理、协议转换、状态跟踪、资源隔离——全压进 Nginx 这个久经考验的事件驱动引擎里。Nginx 本身在 Ubuntu 22.04 上已深度适配 systemd、AppArmor 和内核 TCP 栈优化,而 RTMP 模块只是在其 upstream 机制上做了轻量扩展,不碰主线程模型,也不 fork 子进程。这意味着:单机 2C4G 的 VPS 就能稳稳支撑 80+ 路 RTMP 推流(实测峰值带宽 120 Mbps),HLS 切片延迟稳定在 8–12 秒,且 CPU 峰值长期压在 35% 以下。这不是理论值,是我们在线上跑了 17 个月的真实数据。
关键区别在于架构层级。FFmpeg 是“流处理器”,本质是命令行工具链,它处理的是“一帧一帧的数据”,但不管“谁在连、连了多久、断了几次、带宽多少”。而 Nginx-RTMP 是“流媒体网关”,它天然具备连接上下文:每个 RTMP 客户端连接进来,模块会生成唯一 session ID,记录推流名、时间戳、码率、客户端 IP、User-Agent(如果推流端带了)、甚至首帧 PTS;这些信息全暴露在/stat页面里,还能通过on_publish/on_play回调脚本实时干预。比如我们加了一段 Python 脚本,在on_publish阶段校验推流密钥(从 Redis 读取),非法推流直接拒绝,整个过程耗时 < 8ms,完全不影响推流握手速度。这种能力,FFmpeg 做不到,也没法优雅地做。
再看生态兼容性。Ubuntu 22.04 默认源里的nginx是 1.18 版本,但 Nginx-RTMP 模块要求 Nginx ≥ 1.2.6 且需重新编译。很多人卡在这一步,以为必须手动下载源码、装一堆 dev 包、改 configure 参数。其实大可不必——我们验证过,用 Ubuntu 官方提供的nginx-full包(含所有常用模块)作为基础,仅需 patch 两个小文件就能启用 RTMP,全程不碰./configure。这个细节后面会细说。现在先明确一点:你不是在“装一个流媒体软件”,而是在 Ubuntu 22.04 的成熟 Web 服务底座上,“打开一个流媒体开关”。这个认知差,决定了你是花三天调通,还是花三周反复重装系统。
提示:别被“RTMP 已淘汰”的说法带偏。HLS/DASH 确实更适合公网 CDN 分发,但 RTMP 在局域网推流、OBS 直连、低延迟互动场景仍是事实标准。Nginx-RTMP 不是让你只用 RTMP,而是以 RTMP 为入口,自动转出 HLS、DASH、FLV、甚至 JPEG 截图,它是个协议翻译中枢,不是协议孤岛。
2. 编译前的系统级准备:绕开 Ubuntu 22.04 的三个默认陷阱
Ubuntu 22.04 LTS(Jammy Jellyfish)的底层变化比表面看起来深刻得多。它默认启用systemd-resolved做 DNS 解析,内核升级到 5.15,iptables被nftables全面接管,而最关键的——nginx包从nginx-light/nginx-full拆分为nginx-core+nginx-modules架构。这三点不提前处理,编译 Nginx-RTMP 时会遇到三类典型报错:undefined reference to 'clock_gettime'(链接器找不到实时库)、error: ‘struct sockaddr_storage’ has no member named ‘ss_family’(内核头文件版本错配)、以及最让人抓狂的nginx: [emerg] unknown directive "rtmp"(模块根本没加载成功)。
第一个陷阱:clock_gettime链接失败。Ubuntu 22.04 的libc6-dev默认不链接librt,而 Nginx-RTMP 的ngx_rtmp_cmd_module.c里用了clock_gettime(CLOCK_MONOTONIC, &ts)。解决方法不是加-lrt到 LDFLAGS(那会污染全局),而是精准打补丁:在objs/Makefile生成后、执行make前,用 sed 替换$(LINK) $(LDFLAGS)行,追加-lrt。但更稳妥的做法是,在./configure前设置环境变量:
export LIBS="-lrt" ./configure --add-module=../nginx-rtmp-module --with-http_ssl_module --with-compat注意,这里--with-compat是关键,它让模块能被动态加载,避免重编译整个 Nginx。我们实测发现,漏掉这个参数,即使编译成功,nginx -t也会报module not found。
第二个陷阱:sockaddr_storage成员缺失。这是内核头文件与 Nginx 源码的兼容问题。Ubuntu 22.04 的linux-libc-dev包里,/usr/include/asm-generic/socket.h对ss_family的定义方式变了。Nginx-RTMP 模块的ngx_rtmp_handler.c第 123 行直接访问((struct sockaddr_storage *)sa)->ss_family,但在新头文件下,ss_family被包裹在联合体里。修复只需一行:把原代码
if (sa->ss_family == AF_INET) {改成
if (((struct sockaddr_in*)sa)->sin_family == AF_INET) {——别嫌土,这是最直接有效的绕过方式。我们测试过 12 种 patch 方案,只有这个在 22.04/20.04/18.04 三套系统上全部通过。
第三个陷阱:模块未加载。Ubuntu 22.04 的nginx-full包默认禁用所有第三方模块加载路径。你必须显式配置load_module指令,并确保路径绝对正确。很多教程让你把.so文件丢进/usr/lib/nginx/modules/,但实际路径是/usr/share/nginx/modules/(注意是share不是lib)。更隐蔽的问题是 SELinux/AppArmor 干预:Ubuntu 22.04 默认启用 AppArmor,其abstractions/nginx模板不包含对.so模块的读取权限。临时关闭 AppArmor(sudo systemctl stop apparmor)能快速验证,但生产环境必须加规则:
sudo nano /etc/apparmor.d/local/usr.sbin.nginx # 添加一行: /usr/share/nginx/modules/*.so mr,然后sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx重载。
最后强调一个易忽略的依赖:libpcre3-dev。Ubuntu 22.04 的pcre2库已成主流,但 Nginx-RTMP 仍依赖老版 PCRE1 的pcre_compile函数。不装libpcre3-dev,configure 会静默跳过 PCRE 支持,导致后续location ~* \.(m3u8|ts)$这类正则匹配全部失效。验证方法很简单:编译完运行nginx -V 2>&1 | grep -o pcre,输出应为pcre而非空。
3. 动态模块编译实战:从零构建可热加载的 rtmp.so
现在进入核心操作。我们放弃“从头编译 Nginx”这种重模式,采用 Ubuntu 22.04 官方nginx-full包 + 动态模块加载的轻量方案。全程无需卸载现有 Nginx,不改动任何系统配置,所有操作在普通用户家目录完成,15 分钟内可完成。
第一步:安装基础依赖并获取源码。注意,这里不用apt install nginx,而是装nginx-full的 dev 包,它包含头文件和静态库:
sudo apt update && sudo apt install -y \ build-essential zlib1g-dev libpcre3-dev libssl-dev \ nginx-full nginx-full-dev git curl wgetnginx-full-dev是关键,它提供/usr/src/nginx/下的完整源码树和debian/rules构建脚本。接着拉取 Nginx-RTMP 模块(用官方主仓库,别用 fork):
cd ~ && git clone https://github.com/arut/nginx-rtmp-module.git第二步:打上前面提到的两个补丁。先修复sockaddr_storage问题:
sed -i 's/sa->ss_family/((struct sockaddr_in*)sa)->sin_family/g' \ nginx-rtmp-module/ngx_rtmp_handler.c再修复clock_gettime链接问题,在nginx-rtmp-module/config文件末尾追加:
echo "CORE_LIBS=\"$CORE_LIBS -lrt\"" >> nginx-rtmp-module/config第三步:利用 Ubuntu 的debian/rules构建系统生成动态模块。这是最省心的方式——它复用官方打包脚本,保证 ABI 兼容:
cd /usr/src/nginx sudo make modules \ MODULES_PATH="/usr/share/nginx/modules" \ MODULES="nginx-rtmp-module" \ MODULES_CONF="debian/modules/nginx-rtmp-module.conf"注意MODULES_CONF参数指向一个配置文件,我们需提前创建它:
echo "obj-$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)/nginx-rtmp-module.o" | \ sudo tee /usr/src/nginx/debian/modules/nginx-rtmp-module.conf执行make modules后,会在/usr/src/nginx/objs/下生成ngx_rtmp_module.so。把它复制到模块目录:
sudo cp /usr/src/nginx/objs/ngx_rtmp_module.so /usr/share/nginx/modules/第四步:配置 Nginx 加载模块。编辑/etc/nginx/nginx.conf,在user指令下方、events块之前,添加:
load_module /usr/share/nginx/modules/ngx_rtmp_module.so;然后创建 RTMP 配置文件/etc/nginx/conf.d/rtmp.conf:
rtmp { server { listen 1935; chunk_size 4096; application live { live on; record off; allow publish 127.0.0.1; allow publish 192.168.0.0/16; deny publish all; } } }这里allow publish是安全底线:只允许本机和局域网推流,公网 IP 默认拒绝。别信“先开放再加固”的说法,线上第一行配置就该是防火墙。
第五步:验证模块加载。执行sudo nginx -t,输出必须是syntax is ok且无警告。如果报unknown directive "rtmp",99% 是load_module路径错了或 AppArmor 拦截了。此时运行sudo strace -e trace=openat nginx -t 2>&1 | grep rtmp,能看到 Nginx 实际尝试打开的路径,对照修正即可。
注意:不要用
sudo systemctl restart nginx立即生效。先sudo nginx -s reload,它会平滑重启 worker 进程,不中断已有流。我们线上所有更新都走 reload,零秒切换。
4. RTMP→HLS→DASH 全链路配置:不只是“能播”,而是“播得稳”
Nginx-RTMP 的价值不在 RTMP 本身,而在它能把一路推流,实时、无损、低开销地转出多种格式。很多人只配了hls on就以为完工,结果发现 HLS 播放卡顿、切片丢失、DASH manifest 404——问题全出在参数没吃透。
先看 HLS 配置。这是最常用的 Web 播放格式,但默认参数极不友好:
application live { live on; hls on; hls_path /var/www/html/hls; hls_fragment 3s; hls_playlist_length 30s; hls_nested on; }这段代码有四个致命隐患。第一,hls_path必须是 Nginx worker 进程有写权限的目录,/var/www/html/hls默认属主是www-data,但如果你用sudo nginx -t测试,root 用户能写,worker 却可能因权限不足无法创建文件。解决方案:sudo chown -R www-data:www-data /var/www/html/hls && sudo chmod -R 755 /var/www/html/hls。第二,hls_fragment 3s太短,会导致切片数量爆炸(30 秒 playlist 就要存 10 个.ts文件),磁盘 I/O 压力大,且浏览器缓存效率低。实测5s是平衡点:既保证首屏时间 < 8 秒,又控制文件数在合理范围。第三,hls_playlist_length 30s意味着 m3u8 文件只保留最近 30 秒的索引,但若网络抖动导致某次切片延迟,旧索引被删而新索引未生成,就会出现空白期。我们改为60s,并加hls_cleanup on自动清理过期文件。第四,hls_nested on开启后,切片路径变成hls/streamname/index.m3u8,但很多播放器(如 Video.js)默认不识别嵌套路径,需在 HTML 里显式指定src="hls/streamname/index.m3u8",不如关掉,用扁平路径。
修正后的 HLS 配置:
application live { live on; hls on; hls_path /var/www/html/hls; hls_fragment 5s; hls_playlist_length 60s; hls_cleanup on; hls_nested off; hls_continuous on; # 关键!避免切片间隙 hls_sync 100ms; # 强制音视频 PTS 对齐,防音画不同步 }再看 DASH。它比 HLS 更复杂,因为需要生成 MPD(Media Presentation Description)文件和分片。Nginx-RTMP 的 DASH 支持是实验性的,但经过我们 11 个月压测,已足够稳定:
application live { live on; dash on; dash_path /var/www/html/dash; dash_fragment 5s; dash_playlist_length 60s; dash_cleanup on; }注意,DASH 和 HLS 可以共存于同一 application,Nginx 会自动为同一推流生成两套切片。但dash_path和hls_path必须不同目录,否则文件名冲突。DASH 的dash_fragment必须与 HLS 的hls_fragment严格一致,否则 MPD 里的duration字段会错乱,播放器解析失败。我们曾因hls_fragment 5s而dash_fragment 4s,导致 dash.js 报Invalid MPD: duration mismatch,排查了 6 小时才发现是这个参数。
最后是 FLV 直播流。很多人忽略它,但它对低延迟场景至关重要:
application live { live on; play /var/www/html/flv; # 指向静态文件目录 }这行配置让 Nginx-RTMP 开启 HTTP-FLV 服务,URL 为http://your-server/flv?stream=streamname。OBS 或 ffmpeg 可直接用rtmp://your-server/live/streamname推流,前端用 flv.js 播放,端到端延迟压到 1.2 秒以内(实测数据)。flv.js 的优势是纯 JS 实现,不依赖 MSE,兼容 IE11,且启动快。我们对比过 hls.js 和 flv.js:相同网络下,flv.js 首帧时间平均快 2.3 秒,卡顿率低 67%。
提示:所有切片目录(
hls_path,dash_path,play目录)必须在 Nginx 配置中显式声明为 location:location /hls { types { application/vnd.apple.mpegurl m3u8; } root /var/www/html; add_header Cache-Control no-cache; }否则浏览器会因 MIME 类型错误拒绝加载 m3u8。
5. 生产级加固:从“能跑”到“敢上线”的七道防线
配置完基本功能,离真正上线还差很远。我们总结出七道必须落地的防线,每一道都来自真实故障教训。
第一道:连接数与带宽硬限。Ubuntu 22.04 默认ulimit -n是 1024,Nginx worker 进程最多打开 1024 个文件描述符,而每个 RTMP 连接至少占 3 个 fd(socket、log、hls file),100 路推流就爆了。修改/etc/security/limits.conf:
www-data soft nofile 65536 www-data hard nofile 65536并在/lib/systemd/system/nginx.service的[Service]段加:
LimitNOFILE=65536 Restart=on-failure RestartSec=10然后sudo systemctl daemon-reload && sudo systemctl restart nginx。
第二道:推流认证。allow publish只能按 IP 过滤,无法防密码泄露。我们在on_publish回调里集成 JWT 验证:
application live { live on; on_publish http://127.0.0.1:8080/auth; }后端用 Python Flask 写一个/auth接口,解析 URL 中的?token=xxx,校验签名和有效期,返回200 OK或403 Forbidden。Nginx-RTMP 会阻塞推流握手直到回调结束,超时设为 3 秒(on_timeout 3s),避免拖慢正常推流。
第三道:自动录制与归档。教育直播必须存档。record all会录所有流,但磁盘会撑爆。我们用record_unique on生成带时间戳的文件名,并加record_max_size 1G限制单文件大小:
application live { live on; record all; record_path /var/www/html/record; record_suffix -%d-%b-%y-%H-%M-%S.flv; record_unique on; record_max_size 1G; record_interval 3600s; # 每小时切一个文件 }record_suffix里的%d-%b-%y是日期,%H-%M-%S是时间,生成streamname-01-Jan-23-14-30-22.flv,方便按天归档。
第四道:HTTP 状态页与监控。/stat页面暴露所有连接详情,但默认只允许 127.0.0.1 访问。我们加一层 Basic Auth:
location /stat { rtmp_stat all; rtmp_stat_stylesheet stat.xsl; auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; }用openssl passwd -apr1生成密码,存入.htpasswd。这样运维人员可随时查在线人数、码率、延迟。
第五道:日志精细化。默认access_log只记 HTTP 请求,RTMP 推拉流不记录。加rtmp_log指令:
rtmp_log /var/log/nginx/rtmp.log main; log_format main '$remote_addr - $remote_user [$time_local] ' '"$command $app/$flashver" $status $bytes_sent ' '"$session_time" "$bytes_received" "$connect_time"';$command是publish或play,$session_time是会话时长,$connect_time是握手耗时——这些字段对定位卡顿、断连问题至关重要。
第六道:防火墙白名单。Ubuntu 22.04 默认用ufw,必须精确放行:
sudo ufw allow from 192.168.1.0/24 to any port 1935 # 局域网推流 sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw deny 1935 # 拒绝公网 RTMP第七道:定期健康检查脚本。我们写了一个check_rtmp.sh,每 5 分钟用curl -I http://localhost/hls/streamname/index.m3u8检查 HLS 是否可访问,失败则发邮件告警。脚本放在/etc/cron.d/,简单有效。
6. 推流与播放端实操:OBS、FFmpeg、Web 三端联调指南
配置再完美,推流和播放端出错也白搭。我们整理出三端最简、最稳的联调方案,覆盖 95% 场景。
OBS Studio 推流(Windows/macOS/Linux 通用)
这是最常用场景。OBS 设置要点:
- 服务:选择“自定义”
- 服务器:
rtmp://your-server-ip/live(注意结尾是/live,不是/live/streamname) - 流密钥:填
streamname(如class101) - 关键设置:
- 视频编码器:
x264(别用 NVENC,OBS 28+ 对 NVENC 的 RTMP 封装有 bug) - 码率:
2500 kbps(1080p)或1200 kbps(720p) - 关键帧间隔:
2s(必须等于 Nginx 的hls_fragment) - 预设:
veryfast(平衡质量与 CPU) - 音频:
AAC,码率128 kbps,采样率44.1kHz
- 视频编码器:
推流后,立刻访问http://your-server-ip/stat,看到publish状态为active,bytes数字在跳动,说明成功。
FFmpeg 推流(Linux/macOS 终端)
适合自动化或无人值守场景。命令模板:
ffmpeg -re -i /path/to/video.mp4 \ -c:v libx264 -preset veryfast -b:v 2500k -maxrate 2500k \ -g 100 -sc_threshold 0 \ -c:a aac -b:a 128k \ -f flv "rtmp://your-server-ip/live/streamname"关键参数解释:
-re:按原始帧率读取,避免 ffmpeg 疯狂刷帧-g 100:GOP 长度,100 帧 ≈ 2 秒(假设 50fps),必须匹配hls_fragment-sc_threshold 0:禁用场景切换检测,防止 GOP 错乱-maxrate:硬限码率,防突发流量打爆带宽
实测发现,不加-sc_threshold 0,某些 MP4 文件推流后 HLS 切片会频繁中断,日志报hls: fragment not closed。
Web 端播放(HTML5 兼容方案)
必须兼顾现代浏览器和老旧设备。我们采用双播放器策略:
- Chrome/Firefox/Safari:用
hls.js播 HLS - IE11/Edge Legacy:用
flv.js播 HTTP-FLV
HTML 示例:
<div id="video-container"> <video id="video" controls autoplay></video> </div> <script src="https://cdn.jsdelivr.net/npm/hls.js@1.4.2"></script> <script src="https://cdn.jsdelivr.net/npm/flv.js@1.8.2"></script> <script> const video = document.getElementById('video'); const streamName = 'streamname'; let hls, flv; // 检测 HLS 支持 if (Hls.isSupported()) { hls = new Hls(); hls.loadSource(`http://your-server/hls/${streamName}/index.m3u8`); hls.attachMedia(video); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { // Safari 原生 HLS video.src = `http://your-server/hls/${streamName}/index.m3u8`; } else { // 降级到 FLV flv = flvjs.createPlayer({ type: 'flv', url: `http://your-server/flv?stream=${streamName}`, isLive: true, enableStatis: false, }); flv.attachMediaElement(video); flv.load(); } </script>注意enableStatis: false关闭统计上报,减少额外请求。isLive: true启用直播模式,自动处理追帧。
最后分享一个血泪经验:所有播放 URL 必须用
http://,别用https://,除非你已配好 TLS 证书。Nginx-RTMP 的 HLS 输出不支持 HTTPS 重定向,浏览器会直接报Mixed Content错误。我们曾因在开发环境用http,上线后切https却忘了改播放地址,导致全站直播黑屏 2 小时。
7. 故障排查黄金链路:从“播不了”到“根因定位”的完整路径
线上最怕的不是报错,而是“一切看似正常,但就是播不了”。我们梳理出一条标准化排查链路,按顺序执行,90% 的问题 10 分钟内定位。
第一步:确认 Nginx-RTMP 模块已加载
执行sudo nginx -V 2>&1 | grep -o rtmp,输出应为rtmp。若为空,检查load_module路径和 AppArmor 权限。
第二步:检查 RTMP 端口是否监听
sudo ss -tlnp | grep :1935应看到nginx: master process。若无输出,检查rtmp { server { listen 1935; } }是否在配置中,且nginx -t通过。
第三步:验证推流是否到达 Nginx
用tcpdump抓包:
sudo tcpdump -i any -nn port 1935 -w rtmp.pcap推流 10 秒后停止,用 Wireshark 打开rtmp.pcap,过滤rtmp,应看到connect、createStream、publish等握手包。若只有connect没有publish,说明推流端被allow publish拦截。
第四步:检查 HLS 切片是否生成
ls -la /var/www/html/hls/streamname/应有index.m3u8和若干*.ts文件。若无,检查hls_path权限和hls on是否开启。
第五步:验证 m3u8 文件内容
curl http://your-server/hls/streamname/index.m3u8输出应为标准 m3u8 格式,包含#EXTINF:5.000,和streamname-xxxx.ts。若返回 404,检查 Nginx 的location /hls配置和 MIME 类型。
第六步:用 VLC 直播测试
VLC 是终极验证工具:
- 打开 VLC → 媒体 → 打开网络串流 → 输入
rtmp://your-server/live/streamname - 若能播,说明 RTMP 正常;再输
http://your-server/hls/streamname/index.m3u8,若能播,说明 HLS 正常。VLC 不受浏览器 CORS 限制,是排除前端问题的利器。
第七步:分析 Nginx 日志
重点看/var/log/nginx/error.log:
client denied by rule:allow publish规则拒绝hls: fragment not closed:GOP 设置错误stat: failed to open file:hls_path权限不足on_publish timeout:认证接口响应超时
我们曾遇到一个诡异问题:error.log里大量hls: fragment not closed,但hls_fragment明明设了 5s。最终发现是推流端(OBS)的keyframe interval设成了0,导致 GOP 不固定。把 OBS 的关键帧间隔从0改成2s,问题消失。
提示:所有排查步骤必须按顺序执行,跳步只会浪费时间。我们内部 SOP 规定,运维接到“播不了”告警,必须严格走完这七步,每步截图留痕。不是为了甩锅,而是让问题可追溯、可复现、可沉淀。
8. 性能压测与容量规划:单机到底能扛多少路流?
“能跑”和“能扛”是两回事。我们用真实压测数据告诉你 Ubuntu 22.04 上 Nginx-RTMP 的真实边界。
测试环境:
- 云服务器:2 核 4G,SSD 磁盘,100 Mbps 带宽
- 推流端:10 台机器,每台用 FFmpeg 循环推 720p@1200kbps 视频
- 播放端:200 个 Chrome 标签页,用 hls.js 播放同一
index.m3u8
压测结果:
| 并发推流路数 | CPU 使用率 | 内存占用 | HLS 延迟 | 切片丢失率 |
|---|---|---|---|---|
| 20 | 22% | 1.1G | 7.2s | 0% |
| 50 | 48% | 1.8G | 8.1s | 0.02% |
| 80 | 76% | 2.9G | 9.5s | 0.15% |
| 100 | 92% | 3.7G | 12.3s | 1.8% |
关键发现:
瓶颈在磁盘 I/O,不在 CPU。当推流 > 60 路时,
iostat -x 1显示await(平均等待时间)从 1ms 升至 15ms,%util达 98%。这是因为每个.ts文件写入都是小 IO,HLS 切片频率高,SSD 也扛不住。解决方案:把hls_path挂载到内存盘(tmpfs):sudo mount -t tmpfs -o size=2G tmpfs /var/www/html/hls sudo chown -R www-data:www-data /var/www/html/hls压测后,100 路时
await降至 0.3ms,延迟稳定在 8.5s,丢失率归零。带宽不是线性增长。100 路 1200kbps 推流,理论带宽 120 Mbps,但实测出口带宽仅 85 Mbps。原因是 Nginx-RTMP 的 HLS 转码有压缩冗余,且 TCP 传输有开销。我们按“理论带宽 × 0.7”做容量规划,留足缓冲。
连接数有隐性上限。Ubuntu 22.04 的
net.core.somaxconn默认 128,当并发连接 > 100 时,新连接会排队。需调大:echo 'net.core.somaxconn = 4096' | sudo tee -a /etc/sysctl.conf sudo sysctl -p
容量规划公式:
所需服务器数 = ceil(总推流路数 × 1.2 / 单机安全路数)其中单机安全路数 = 60(H