不能用os.readfile读完再写,因会内存暴涨、首帧延迟高、阻塞全链路且不支持range请求;必须用io.copy流式透传,配合http.newresponsecontroller控制超时与中断。

直接用 os.ReadFile 加 http.ResponseWriter.Write 做短视频分发,100 个并发就可能 OOM;必须流式透传、连接级控制、资源复用,否则上线即雪崩。
为什么不能用 os.ReadFile 读完再写
这是最常踩的坑:看似简单,实则破坏所有关键指标。
- 内存暴涨:每个请求都复制一份完整视频(如 200MB 短剧),100 并发 ≈ 占用 20GB 内存,Go GC 来不及回收
- 首帧延迟不可控:必须等整个文件读完才开始响应,用户看到第一帧要等数秒甚至更久
- 慢客户端阻塞全链路:TCP 缓冲区满后
Write阻塞,goroutine 卡死,后续请求排队等待 - 不支持
Range请求:浏览器拖拽、倍速、暂停续播全部失效,播放体验断层
必须用 io.Copy + http.NewResponseController 控制流
Go 1.22+ 提供的 http.NewResponseController 是视频分发的生命线,它让写入可中断、可超时、可监控。
- 用
rc.SetWriteDeadline(time.Now().Add(30 * time.Second))防止单个卡顿连接拖垮整个服务 - 用
rc.Conn().SetReadDeadline防止客户端长期不读导致服务端缓冲堆积 -
io.Copy(w, f)底层自动使用 32KB buffer 分块传输,无需手动封装bufio.Writer - 务必在
os.Open后立即调用f.Stat(),并设置Content-Length和Accept-Ranges: bytes
常驻进程下如何防 goroutine 泄漏和 fd 耗尽
常驻不是目的,可控才是。裸跑常驻进程比短生命周期更危险。
- 必须设
http.Server.ReadTimeout、WriteTimeout(建议 ≤ 30s),且IdleTimeout≤ 90s - 所有流逻辑包裹在
context.WithTimeout(r.Context(), 60*time.Second)中,禁止无约束循环 - 视频分片 buffer 用
sync.Pool复用,禁用make([]byte, 2*1024*1024)这类大块分配 - 用
net/http/pprof暴露/debug/pprof/goroutine?debug=2,定期检查异常增长
热更新元数据而不中断播放的关键点
短剧上下架、CDN 切换、权限调整频繁,硬重启等于主动制造播放事故。
- 元数据(如
short_id → oss_path映射)存在sync.Map,监听文件变更或 etcd watch 触发 reload - 路由 handler 替换必须原子:新建
http.ServeMux或自定义http.Handler,用指针替换旧实例 - 配置结构体 +
atomic.Value,读取时config.Load().(*Config),避免全局变量竞态 - 暴露
/healthz?reload=metadata管理端点,配合灰度发布验证更新效果
真正难的不是“怎么把视频发出去”,而是当 5000 个连接同时拖拽、倍速、切片、重连时,每个 io.Copy 是否还在受控节奏里运行——这取决于你是否在打开文件前就设好了 deadline,是否在每次 buffer 分配前查了 pool,是否在 reload 元数据时锁住了不该锁的东西。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











