树莓派跑了个 M3U8 下载服务,内存从 600MB 降到 2MB

树莓派跑了个 M3U8 下载服务,内存从 600MB 降到 2MB

最近在树莓派上跑了个 M3U8 下载服务,用 Rust 写的。跑了一周发现内存涨到 237MB,峰值冲到过 607MB,差点把树莓派撑死。

先说说这个服务是干嘛的。就是提供一个 Web 页面,贴个 M3U8 链接进去,服务端下载完转成 MP4。用油猴脚本配合的话,浏览视频网站时点一下就把链接发给服务器了,不用在自己电脑上下载。

问题的根源

查了两天代码,发现三个要命的地方:

1. 直传模式的内存泄漏

服务有个"直传"功能,下载的同时通过浏览器传回给用户。代码里用了一个 BTreeMap 缓存乱序到达的视频片段,但没有任何大小限制。如果片段 0 下载慢了,后面 139 个片段全堆在内存里。一个片段 2-5MB,堆 100 多个就是 300-700MB。

修复很简单,加了个 max_buffered 上限,超出就 sleep 等待消费掉再继续:

rust 复制代码
let max_buffered = self.concurrent.saturating_mul(2).max(8);
if buffer.len() >= max_buffered {
    tokio::time::sleep(Duration::from_millis(100)).await;
}

2. 回调风暴

每下载完一个片段就触发一次进度更新,每次更新都 spawn 一个异步任务。140 个片段 × 20 次更新 = 2800 个短暂任务,调度开销全变成了堆碎片。

改成 250ms 节流:用 AtomicU64 记录上次更新时间,间隔不足就跳过。

3. 合并时整段读入内存

下载完成后合并片段时,每个片段都 read_to_end 读到 Vec 里再写出去,大片段一把就吃掉 10MB。

改成 tokio::io::copy 流式拷贝,64KB 缓冲区搞定。

修完后的效果

改了之后重启,空载时常驻内存从 237MB 掉到 1MB:

bash 复制代码
# 优化前(跑了一周)
sudo systemctl status down
Memory: 237.6M (peak: 607.9M)

# 优化后(刚启动,空载)
sudo systemctl status down
Memory: 1.0M (peak: 1.7M)

跑了几个下载任务之后再测,1.5 小时常驻 146MB,峰值 273MB------比起旧版同期数据(启动就 200M+,跑着跑着奔 607M)已经稳定太多了:

bash 复制代码
# 优化后(1.5 小时,跑过多次下载任务)
sudo systemctl status down
Memory: 146.2M (peak: 273.3M)

再也不用担心树莓派 OOM 了。

另外还加了个 Content-Type 检查:有些 M3U8 链接过期后服务器返回 HTML 错误页,以前要重试 4 次等 15 秒才报错,现在 1 秒内快速失败。

用到的技术栈

  • Rust + axum(Web 框架)
  • tokio(异步运行时)
  • reqwest(HTTP 客户端)
  • m3u8-rs(M3U8 解析)
  • FFmpeg(TS 转 MP4)

代码放 GitHub 了:https://github.com/Sunrisies/m3u8_download

感兴趣的可以 clone 下来,cargo build --release 编出来 5MB,scp 到树莓派上就能跑。依赖就一个 FFmpeg(用来合并转码),其他全静态编译。

相关推荐
运维行者_2 小时前
应用性能监控怎么选?从调用链到用户体验的五个能力维度
运维·服务器·数据库·人工智能·mysql·性能优化·应用性能监控
leeyi11 小时前
缓存同步之后,还要考虑没收到消息的节点
后端·性能优化·erlang
程工造Agent13 小时前
从 0 到 1 学 Codex 源码:顺着 main 读懂 Rust 闭包与 async move
rust·ai编程
shimly12345618 小时前
(done) RUST 指针详解
rust
看浪的路人18 小时前
第9讲:记忆系统的性能优化与基准测试——缓存策略、连接池、批量操作与压测方法论
缓存·性能优化
geovindu19 小时前
rust: Template Method Pattern
开发语言·后端·设计模式·rust·行为模式·模板方法模
MoRanzhi120319 小时前
第4篇 · 使用 PyQt5 实现 2048:平滑动画与性能优化
python·性能优化·qtimer·pyqt5·qpainter·gui动画·2048游戏
余槐i20 小时前
yt-dlp 线程开到 16 反而更慢:1000 个视频的实测与限速原因
性能优化·ffmpeg·自动化·开发工具·yt-dlp
西红柿炖牛腩35420 小时前
3D 模型压缩脚本又报错又配环境?换个不用维护的方案
javascript·3d·性能优化
打工仔折腾 AI20 小时前
极空间部署Portainer实战:绕过Socket权限、解决9000端口冲突与固定内网访问
人工智能·后端·python·性能优化