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

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

朋友公司的文件服务,一到月底报表下载高峰期就崩。运维排查发现每次下载一个 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....

Spring Cloud Gateway 大流量内存溢出防护:拒绝全量缓冲,流式转发抗住高并发!

Spring Cloud Gateway 大流量内存溢出防护:拒绝全量缓冲,流式转发抗住高并发!

在微服务架构中,API 网关是系统的入口,承担着请求路由、负载均衡、安全认证等重要职责。随着业务流量的不断增长,API 网关面临着越来越大的挑战,其中最常见的问题之一就是大流量下的内存溢出。 想象一下这样的场景:当系统遭受突发流量冲击时,Gateway 接收到大量的请求,每个请求都需要处理和转发。如果 Gateway 采用全量缓冲的方式处理请求体,那么在大流量下,内存使用量会急剧上升,最终导致内存溢出,系统崩溃。 今天我就跟大家分享一套基于 Spring Cloud Gateway 的大流量内存溢出防护方案,通过流式转发替代全量缓冲,让 Gateway 在高并发下依然稳定运行。 为什么会内存溢出? 先来说说 Gateway 内存溢出的根本原因。在默认情况下,Spring Cloud Gateway 处理请求时会: 全量读取请求体:将整个请求体读取到内存中 创建完整的请求对象:为每个请求创建包含完整请求体的对象 缓冲响应数据:将后端服务的响应也完整缓冲到内存中 内存使用无上限:当请求体较大或并发请求较多时,内存使用量会持续增长 特别是在以下场景中,内存溢出的风险更高: 大文件上传:....

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