文章 555
评论 5
浏览 199883
大文件下载内存溢出防护:拒绝全量加载,零拷贝流式输出抗住万级并发

大文件下载内存溢出防护:拒绝全量加载,零拷贝流式输出抗住万级并发

朋友公司的文件服务,一到月底报表下载高峰期就崩。运维排查发现每次下载一个 200MB 的 Excel,后端代码里居然是 byte[] data = file.readAllBytes()——把整个文件加载到堆内存里再往外写。10 个人同时下载,就是 10 个 200MB 的数组在堆里,JVM 直接 OOM。重启后又崩,加内存也撑不住——因为下载峰值不是你能通过加内存解决的线性问题。 大文件下载的 OOM 问题本质就一句话:你把整个文件搬进了 JVM 堆里,但 JVM 堆不是为文件 IO 设计的。 文件应该从磁盘流到网卡,中间经过你的应用,但不要在你的堆里停留。 今天聊聊怎么用流式传输和零拷贝,让 X GB 的文件下载跟 X KB 的一样轻松。 错误姿势:全量加载 最常见的错误写法: @GetMapping("/download") public ResponseEntity<byte[]> download(String filePath) { byte[] data = Files.readAllBytes(Path.of(filePath)); // ❌ 全量加载到....

大文件下载内存溢出防护:拒绝全量加载,零拷贝流式输出抗住万级并发!

大文件下载内存溢出防护:拒绝全量加载,零拷贝流式输出抗住万级并发!

做文件下载功能的同学肯定都遇到过这个问题:用户下载一个大文件,结果服务器内存飙升,最后 OOM 直接崩溃。特别是在处理视频、备份文件、日志压缩包等大文件时,这个问题尤为突出。 我之前就遇到过这样一个案例:一个用户反馈下载一个 5GB 的视频备份文件时,服务器直接宕机了。排查后发现,代码里居然是这样写的: @GetMapping("/download/{fileId}") public byte[] download(@PathVariable Long fileId) { File file = fileService.getFile(fileId); return Files.readAllBytes(file.toPath()); // 一次性加载到内存! } 这就是典型的"小文件思维"写大文件代码的案例。在低并发场景下可能没问题,但一旦并发上来,内存就会爆掉。 今天我们就来聊聊大文件下载的正确姿势,让你的系统轻松抗住万级并发。 大文件下载的内存问题根源 1. 传统方式的致命缺陷 很多开发者习惯用 Files.readAllBytes() 或者 FileInputStream.r....

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程