一、前言在日常系统开发中文件下载是非常常见的一类功能例如导出报表下载附件下载压缩包下载模板文件下载安装包很多项目会基于Spring MVC 静态资源映射能力实现下载功能例如registry.addResourceHandler(/download/**).addResourceLocations(file:/data/files/);用户通过/download/a.zip即可直接下载服务器文件。这类接口与普通 JSON 业务接口存在明显区别通常没有复杂业务逻辑不涉及大量数据库操作更多消耗网络、磁盘、IO 和连接资源因此文件下载接口的性能测试思路与普通接口 TPS 压测并不完全相同。本文将结合一个典型场景“一个页面存在两个不同文件下载链接”系统讲解Spring MVC 静态资源下载接口的特点与性能测试思路下载类接口需要重点关注的核心指标缓存机制对下载性能测试结果的影响多文件下载场景下的性能测试方案设计二、Spring MVC 静态资源映射接口简介Spring MVC 静态资源映射本质上是将某个 URL 路径映射到服务器文件目录。例如ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){registry.addResourceHandler(/download/**).addResourceLocations(file:/data/files/);}}此时/download/a.zip会映射到/data/files/a.zip这种方式实现简单适用于公共文件下载模板文件下载安装包下载静态资源下载其特点是业务逻辑较少请求处理链路较短更依赖服务器 IO 与网络能力三、下载接口与普通业务接口的区别很多人在做性能测试时会默认关注TPSQPS响应时间但对于下载类接口而言真正关键的指标往往并不是 TPS。1. 普通业务接口例如/api/user/list通常消耗 CPU消耗数据库连接涉及业务计算涉及缓存查询性能瓶颈通常在CPU数据库RedisJVM GC2. 文件下载接口例如/download/a.zip通常不涉及复杂计算不涉及数据库更依赖文件读取与网络传输性能瓶颈通常在资源类型是否关键网络带宽非常关键网卡吞吐非常关键TCP连接数非常关键磁盘 IO关键Tomcat/Nginx 连接数关键CPU反而可能不高JVM GC通常不是重点因此下载接口性能测试本质上更偏向IO 与网络吞吐测试。四、下载接口的数据流转过程理解下载压测之前需要先理解一次下载请求在服务器内部的执行过程。用户请求/download/a.zip服务器内部通常经历磁盘 - 内存 - 网卡 - 网络 - 用户具体过程如下1. 磁盘读取文件服务器从/data/files/a.zip读取文件。此时会产生磁盘 IO文件读取操作2. 文件进入内存文件读取后会进入 Linux 内存缓存Page Cache。后续再次访问时可能无需再次读取磁盘。3. 数据通过网卡发送服务器最终通过网卡网络带宽将文件发送给客户端。因此下载接口本质上是服务器持续向外发送大量数据。五、下载接口性能测试的重点指标下载类接口的重点指标通常包括指标说明下载成功率是否存在超时、断连平均下载耗时下载整体时长网络吞吐量MB/s、Gbps并发连接数TCP连接占用情况磁盘 IO文件读取压力网卡带宽利用率是否打满带宽Tomcat/Nginx 连接数是否达到上限其中网络吞吐量与带宽利用率通常是重点。六、下载接口中的缓存机制与性能测试影响下载类接口性能测试中一个非常容易被忽略的问题就是缓存会直接影响压测结果。尤其是基于 Spring MVC 静态资源映射实现的文件下载接口通常都会受到 Linux 文件缓存机制的影响。如果不理解缓存机制很容易出现第一次压测很慢 后面压测越来越快但实际上系统性能未必真的提升了。1. 下载请求的数据流转过程以/download/a.zip为例。一次文件下载请求在服务器内部通常会经历磁盘 - 内存 - 网卡 - 网络 - 用户具体过程如下阶段说明磁盘读取真实文件内存文件进入 Linux Page Cache网卡将数据发送到网络用户客户端接收文件其中磁盘读取速度最慢内存读取速度远高于磁盘网卡负责真正向外发送数据因此如果文件已经进入内存缓存后续下载性能会明显提升。2. 什么是热缓存Warm Cache / Hot Cache第一次下载文件时磁盘 - 内存 - 网卡系统需要真实读取磁盘文件。但 Linux 会自动将热点文件缓存到OS Page Cache后续再次下载时可能直接变成内存 - 网卡无需再次读取磁盘。这种场景称为热缓存场景Warm Cache / Hot Cache3. 热缓存对压测结果的影响进入热缓存后系统通常会出现现象原因下载速度提升不再读取磁盘响应时间下降直接命中内存磁盘 IO 降低文件已缓存CPU 变化不明显下载主要消耗 IO 与网络因此很多下载文件的性能测试最终测到的实际上是内存缓存能力 网络吞吐能力而不是磁盘读取能力。4. 压测中的缓存到底是在压测机还是被测机这是下载压测中最容易混淆的问题。很多人会误以为后面的请求变快 是不是压测机缓存了文件实际上对于 Apache JMeter 这类压测工具而言通常只是不断发起 HTTP 请求。真正发生缓存的位置通常是缓存位置是否关键被测机 OS Page Cache非常关键Nginx Cache非常关键CDN Cache非常关键压测机缓存一般不关键也就是说即使是500个不同用户同时下载同一个文件Linux 仍然只需要第一次读磁盘后续所有请求都可以共享同一份Page Cache因此即使模拟的是“不同用户”也依然会命中热缓存。这是 Linux 操作系统的正常行为。5. 为什么热缓存是正常且合理的生产场景很多人会担心命中缓存是不是压测不真实实际上真实生产环境中热点文件本来就会被缓存。例如缓存类型场景Linux Page Cache高频下载文件Nginx Cache静态资源CDN Cache公网文件下载因此对于以下场景页面文件下载模板下载安装包下载高频附件下载通常更推荐热缓存场景压测。因为这更符合真实生产环境。6. 如何判断当前是否命中缓存1观察磁盘 IO推荐使用iostat-x1重点关注指标含义rkB/s磁盘读吞吐awaitIO等待时间util磁盘利用率例如现象说明rkB/s 很高正在读磁盘rkB/s 很低可能命中缓存2观察 Linux Cached使用free-m或者cat/proc/meminfo观察Cached字段。如果下载后Cached 持续增长通常说明文件已经进入Page Cache3观察首次与后续下载耗时差异如果第一次下载明显较慢 后续下载明显变快通常说明后续请求已经命中缓存。4判断是否存在 Nginx/CDN 缓存如果系统前面存在NginxCDN网关缓存还需要进一步确认请求是否已经被上层缓存拦截。例如curl-Ihttps://xxx/download/a.zip观察响应头Header说明X-Cache: HIT已命中缓存Age缓存时间Via代理/CDN信息七、下载接口如何进行性能测试1. 压测工具选择通常使用 Apache JMeter 对下载接口进行并发压测用于模拟多用户同时下载长连接占用场景大文件持续传输网络带宽压力场景下载类接口本质是“数据流传输”因此工具重点在于稳定发起请求并持续接收响应而不是复杂业务处理能力。2. JMeter 请求配置1使用 HTTP Request 直接访问下载地址例如GET /download/a.zip无需额外参数处理按真实用户访问方式发起请求即可。2禁止保存响应内容下载接口返回通常为ZIPPDFExcel大文件流如果开启以下功能保存 Response DataView Results TreeDebug Sampler会导致问题原因JMeter 内存占用暴涨响应内容被完整加载CPU 占用升高大文件解析与写入压测机磁盘压力增加结果文件落盘最终结果是压测瓶颈从“被测系统”转移到“压测机”。因此建议关闭响应体保存禁用 View Results Tree仅保留基础统计报告Summary / Aggregate Report3下载结果校验方式下载接口通常不返回业务 JSON 响应体因此其正确性校验不能依赖字段而是根据不同测试目标采用不同粒度的校验方式。HTTP状态码校验基础必选用于判断请求是否正常到达服务端并完成处理200下载成功4xx权限不足或资源不存在5xx服务端异常该校验是所有下载压测的基础条件。响应大小校验常用校验方式通过文件大小判断下载结果是否异常主要用于识别以下问题返回错误页面通常只有几 KB下载过程中发生中断或截断文件未完整传输该方式实现成本低适用于常规性能压测与稳定性测试场景。MD5哈希校验内容一致性校验在 Apache JMeter 中可通过Save response as MD5 hash获取响应内容的哈希值用于校验文件内容是否发生变化。该方式用于验证文件内容是否完全一致是否发生截断但状态码仍为 200是否被错误页面或异常内容替换适用于生产验收测试文件内容一致性要求较高的场景如安装包、配置文件、报表导出CDN或链路稳定性验证场景八、页面存在两个不同文件下载链接时如何测试假设页面中存在下载链接文件大小文件A1MB文件B500MB并且每次点击只下载一个文件两个链接对应两个独立接口那么该场景本质上是同一个页面下的两个独立下载接口。1. 为什么必须分别测试两个接口因为文件大小不同后性能特征会完全不同。1小文件下载特点例如1MB更容易表现为特征表现TPS 较高是请求频繁是连接建立频繁是Tomcat线程切换频繁是2大文件下载特点例如500MB更容易表现为特征表现带宽占用大是下载耗时长是TCP连接长期占用是磁盘 IO 更明显是因此必须分别压测/download/fileA /download/fileB2. 为什么还推荐混合压测虽然两个接口需要单独测试。但真实生产中两个下载接口最终会共享网络带宽网卡NginxTomcat连接磁盘 IO因此还需要模拟真实用户行为。例如下载类型用户占比文件A70%文件B30%模拟一部分用户下载小文件一部分用户下载大文件观察带宽是否被大文件占满小文件响应是否被拖慢并发连接是否耗尽这类测试通常称为混合场景压测。3. 推荐的完整测试方案建议分三个阶段进行。第一阶段文件A单独压测验证小文件并发能力TPSTomcat连接能力第二阶段文件B单独压测验证大文件带宽能力网络吞吐能力长连接稳定性第三阶段混合场景压测按真实比例混合文件A下载文件B下载验证不同类型下载是否互相影响系统整体稳定性带宽竞争情况九、总结Spring MVC 静态资源下载接口的性能测试与普通业务接口不同重点通常不在数据库或业务逻辑而是在网络带宽、网卡吞吐、TCP连接数以及磁盘 IO 等资源。下载请求在服务器内部通常会经历“磁盘 - 内存(Page Cache) - 网卡 - 用户”的过程因此下载压测很容易受到 Linux Page Cache 的影响。很多情况下第一次请求会真实读取磁盘后续请求则会进入热缓存场景直接从内存返回数据。所以在下载压测中需要明确当前测试属于冷缓存还是热缓存场景而页面下载、高频文件下载等场景通常更推荐采用热缓存压测因为这更符合真实生产环境。对于“一个页面存在两个不同文件下载链接”的场景建议采用“单接口压测 混合场景压测”的方式。先分别测试不同文件的下载接口分析小文件高并发与大文件长连接、带宽占用等差异再结合真实用户比例进行混合压测验证系统在真实下载场景下的整体稳定性。