☰
大文件下载测试:从网络吞吐到磁盘写入的完整验证
2026/10/29 14:40:31 网站建设 项目流程

做下载测试这件事,看起来特别简单——找个大文件,点下载,看进度条跑完,看一眼速度,完事。但我见过太多人就是这么做的,然后拿着一个"112 MB/s"的数字去汇报,结果被问一句"你测的是网络还是硬盘"就卡住了。大文件下载测试真正的门槛不在操作,而在于你知不知道自己测出来的那个数字代表什么。这篇内容就是把我这几年反复做链路上限验证、存储写入验证、服务端限速验证的那套土办法完整写出来,从造文件、起服务、跑客户端到读数字、排故障,一步一步来,不需要你懂什么高深的东西,会敲几条命令、能看懂单位换算就够。适合谁看?刚接手环境验证任务的运维、需要给设备做验收的测试人员、以及自己搭了台机器想知道它到底能跑多快的普通用户,都能直接抄。

1. 下载测试真正要盯住的四个数字

大部分人做下载测试只记录一个"平均速度",这是最省事也最容易误导自己的做法。一个下载过程里其实至少有四个互相独立的指标在同时变化,它们分别对应链路的不同环节,任何一个出问题都会让最终的"平均速度"变难看,但你从平均值上根本看不出是谁拖的后腿。我一般会把它们分开记:连接建立耗时、首字节时间、瞬时速率曲线、以及落盘完成的总耗时。这四个数字组合起来,才能拼出一张完整的画像。

1.1 平均速率骗人的地方:先看瞬时曲线

平均速率是总字节数除以总耗时,问题在于这个"总耗时"里混进了 TCP 慢启动、服务端读磁盘的冷启动、客户端分配缓冲区等等一堆一次性开销。文件越小,这些开销占比越大,测出来的数字越难看。我做过对比,同样是千兆内网,下载一个 10 MB 的文件平均只有 60 MB/s 出头,换成 1 GB 的文件就能稳定到 110 MB/s 以上——不是网络变快了,是那点固定开销被摊薄了。

所以判断一个下载测试有没有意义,第一个标准就是文件要足够大,让传输过程进入稳态。经验值是至少要让下载持续 10 秒以上,低于这个时长的测试结果我基本不看。而真正有诊断价值的是瞬时曲线:如果曲线是一条平稳的直线,说明链路和磁盘都很健康;如果曲线呈锯齿状反复掉零,多半是磁盘写入跟不上导致接收缓冲区被填满,TCP 窗口被压到零,传输被迫暂停;如果曲线开头很陡后面趋缓,那是正常的慢启动收敛过程,不用管。

抓瞬时曲线最简单的办法不是看图形界面,而是在下载脚本里每秒打一次进度。命令行工具的进度条通常只显示平均速度,看不到瞬时抖动,这一点后面第 4 节会给一段能打印瞬时值的脚本。

1.2 首字节时间和连接耗时,决定了"快不快"的体感

用户说"这个下载好慢",很多时候指的不是吞吐量低,而是点了之后半天没反应。这两个是不同的问题。连接耗时(TCP 握手完成需要多久)和首字节时间(请求发出去到收到第一个字节)反映的是响应性,吞吐量反映的是持续传输能力。做测试时如果只测吞吐,就会漏掉响应性这一块。

我在内网环境里见过这样的情况:文件下载速度能跑到 900 Mbps,非常漂亮,但首字节时间有 800 毫秒。查下来是服务端在处理请求时先做了一次目录遍历和权限校验,这部分逻辑串行执行,每个请求都要等。吞吐量测试完全掩盖了这个问题,因为文件够大,那 800 毫秒被摊到几十秒的传输里去了,占比不到 2%。

所以我的习惯是把首字节时间单独拎出来看,并且用小文件专门测这一项。测吞吐用大文件,测响应用小文件,两套数据分开记,不要混在一起算平均。

1.3 速率的单位陷阱:Mbps 和 MB/s 差八倍

这是新手最容易栽的坑,而且栽了还不自知。网络设备的标称带宽用的是 Mbps(兆比特每秒),下载软件显示的通常是 MB/s(兆字节每秒),两者差 8 倍。千兆网卡理论上限是 1000 Mbps,换算成下载速度是 125 MB/s,实际能跑到 110 到 118 MB/s 就算很健康了。

我整理了一张对照表,做验收的时候直接对着看,省得每次心算:

标称链路带宽理论峰值(MB/s)实测健康区间(MB/s)
100 Mbps12.511.0 ~ 12.0
500 Mbps62.555 ~ 60
1 Gbps125110 ~ 118
2.5 Gbps312.5280 ~ 300
10 Gbps1250900 ~ 1100

注意最后一行的落差明显变大,这不是测错了。万兆环境下瓶颈往往已经不在网络上,而在单条 TCP 流的处理能力、中断分发、或者是磁盘本身,单流跑不满是常态,要跑满得靠多流并发,这个后面会讲。

还有个更隐蔽的单位坑:有些工具用 MiB/s(1024 进制)而不是 MB/s(1000 进制),差 4.8%。做精确对比时一定要看清楚工具用的是什么进制,不然 100 MB/s 和 100 MiB/s 摆在一起会得出错误结论。我一般统一用 MiB/s 记录,因为它和文件大小的显示口径一致,算起来不容易乱。

1.4 磁盘写入:最容易被忽略的短板

下载测试有个天然的歧义:你到底在测网络,还是在测存储?如果数据最后要落到硬盘上,那最终速度取的是"网络吞吐"和"磁盘写入"两者的较小值。我在一台老机器上做过实验,网络能给到 110 MB/s,但目标盘是块用了好几年的机械硬盘,顺序写入只有 90 MB/s 出头,最终下载速度就卡在 88 MB/s 上下,而且曲线抖得厉害。

想分清到底是谁慢,办法很简单:把下载目标指向空设备(相当于把数据直接丢掉,只走网络),对比写真实文件的成绩。两者差距大,说明磁盘是瓶颈;两者接近,说明网络是瓶颈。这个对比我几乎每次做测试都会跑一遍,三十秒的事,能省掉后面几个小时的瞎猜。

磁盘这边还有几个细节要注意。机械硬盘的顺序写入速度受内外圈影响,同一块盘不同位置能差 40%;叠瓦式硬盘在持续写入时缓存用完后速度会断崖式下跌,表现为前 20 秒很快然后突然掉到很低;固态硬盘如果剩余空间不足或者温度过高,也会触发降速。这些现象在下载曲线上的表现都很像,但原因完全不同,得结合磁盘状态一起看。

2. 造一个够大的测试文件,别用稀疏文件糊弄自己

工欲善其事,第一步是准备测试素材。这一步看着最没技术含量,实际上坑最多。我见过有人随手truncate出一个 10 GB 的文件就开始测,结果速度飙到几 GB/s,兴奋了半天才发现那是个稀疏文件,实际占用的磁盘块几乎为零,读的时候内核直接返回零页,压根没碰磁盘。所以造文件这件事必须认真对待,文件大小、内容、是否真实占块,都得确认。

2.1 三种造文件方式的差别

常见的造文件手段有三种,效果天差地别,我把它们的特性列在下面,你可以按测试目的选:

方式命令示例是否真实占块耗时(1 GB)适用场景
分配空间fallocate -l 1G test.bin是(但内容是空洞)毫秒级只测网络,不关心内容
写随机数据dd if=/dev/urandom of=test.bin bs=1M count=1024是30 秒 ~ 数分钟需要真实内容
写零数据dd if=/dev/zero of=test.bin bs=1M count=1024是数秒追求速度,可接受全零

fallocate快到不可思议,因为它只是在文件系统里把块标记为已分配,并不真的写入数据。对于纯粹的吞吐量测试,它其实够用,因为读取时确实要走文件系统、要走磁盘 IO。但如果你的测试链路上有任何环节会做内容相关的处理(比如压缩、去重、内容校验),全零的或者空洞的文件会严重影响结果——压缩类传输会把全零文件压到接近零字节,你测出来的"速度"就没意义了。

我自己的默认选择是/dev/urandom造 1 GB,虽然慢一点,但一劳永逸,后面不管做什么测试都不用担心内容带来的偏差。造完记得用ls -ls确认一下实际占块数,如果第一列显示的块数远小于文件大小,说明被文件系统优化成了稀疏文件,得换方式重造。

2.2 文件内容为什么不能用全零

上面提了一句压缩,这里展开说。现代传输链路里塞满各种透明优化:HTTP 服务端可能开了压缩,中间可能有个做去重的存储,某些协议栈会对相同数据做批量合并。全零文件在这些环节里都会被特殊对待,测出来的数字虚高得离谱。我遇到过一次,用全零文件测出来 2.3 GB/s,换成随机内容后掉到 480 MB/s,差了将近五倍,原因就是链路上有个环节在做内容层面的优化。

就算你的链路确认没有任何压缩和去重,我也不建议用全零。因为随机内容能顺带验证数据完整性——你可以对比源文件和目标文件的校验和,确认传输过程中没有发生位翻转。这在长距离、大文件、高负载的场景下不是杞人忧天,我确实遇到过因为内存故障导致下载文件损坏的情况,如果当时没做校验,这个隐患就一直藏在那里。

2.3 校验和:确认下下来的东西是对的

校验这一步很多人省掉,理由是"文件下下来了能打开就行"。做性能测试可以这么随意,但做验收测试不行。算校验和的成本很低,1 GB 文件在现代机器上几秒钟就完了,收益是你能拍着胸脯说"数据是对的"。

# 在服务端算一次 sha256sum /data/testfiles/test-1g.bin > test-1g.bin.sha256 # 下载完成后在客户端对比 sha256sum -c test-1g.bin.sha256

这里有个小技巧:把校验和文件一起放在服务目录里,客户端下载完文件顺手把校验和文件也拉下来,一条命令就能验证完,不用手工抄写那一长串字符。另外注意,做性能测试的那几轮不要带校验,因为计算校验和本身要消耗 CPU 和内存带宽,会拉低测出来的吞吐量;只有在正确性验证那一轮才做。两件事分开跑,别混着来。

3. 把文件挂出去:本地服务的几种起法

文件造好了,接下来得让客户端能下到它。这一步的坑主要在两个地方:一是服务端自己成为瓶颈,二是服务端的配置让测试结果失真。我用得最多的方案是 Python 自带的 HTTP 服务,够简单,一条命令起来,不需要装任何东西,适合快速验证;如果要把带宽压满或者做长时间稳定性测试,就得换个更结实的方案。

3.1 Python 内置服务,三十秒起一个

Python 3.7 以后自带的服务已经是多线程的了,处理几个并发请求没问题,做单流测试完全够用。起法是这样的:

mkdir -p /data/testfiles cd /data/testfiles python3 -m http.server 8000 --bind 0.0.0.0

--bind 0.0.0.0这个参数不能省。默认它只监听本机回环地址,别的机器根本连不上,新手最常卡在这一步——服务起来了,但客户端一直连接超时,反复检查防火墙也没用,其实就是绑错了地址。想指定目录也可以用--directory /data/testfiles,省得先 cd 过去。

起好之后先在服务端本机自测一下,curl -I http://127.0.0.1:8000/test-1g.bin,能返回 200 和正确的 Content-Length 就说明服务正常。然后再从另一台机器访问,如果本机通、跨机不通,问题一定在网络层(防火墙规则、监听地址、路由),不用去怀疑服务本身。这个由内到外的排查顺序能省下大量时间。

Python 这个方案的天花板在几百兆比特每秒的量级,单流跑到 100 MB/s 左右时 CPU 占用就很可观了。如果目标链路是千兆以上,它自己就会成为瓶颈,这时候必须换更高效的服务端,或者至少确认瓶颈不在服务端——判断方法是看服务端进程的 CPU 占用,如果接近单核跑满,那测出来的数字就是服务端的上限,不是链路的上限。

3.2 想压满带宽时要换的配置项

换用更专业的服务端时,有几个配置项会直接影响下载性能,值得逐一看一眼。第一是零拷贝发送,开启后数据从磁盘到网卡不经过用户态拷贝,能省下大量 CPU 和内存带宽,这个开关在现代服务端里通常是默认开的,但老版本或者某些精简编译版本里可能关着,值得确认。

第二是发送缓冲区大小。缓冲区太小会让 TCP 窗口受限,尤其在长距离、高延迟的链路上,窗口不够直接导致吞吐上不去。第三是 keep-alive,做单流大文件测试时它影响不大,但如果你要测大量小请求的并发能力,keep-alive 开不开结果差好几倍。

还有一点容易被忽略:服务端所在机器的磁盘读性能。测试文件如果是第一次被读,走的是冷缓存,速度受磁盘限制;第二次以后内核会把文件缓存在内存里,直接从内存吐数据,速度快得多,而且极其稳定。这两种情况测出来的数字含义完全不同,我一般会明确标注"冷读"还是"热读"。想强制冷读,可以在两次测试之间清理缓存,但这个操作需要管理员权限且会影响系统上其他业务,在生产机上千万别随便做,测试机上用比较稳妥。

3.3 服务端只看一个日志字段就够了

服务端日志默认会记录每个请求的来源、路径、状态码、响应大小。做下载测试时,我最关心的是响应大小这一个字段,因为它能明确告诉你服务端到底发出去了多少字节。如果客户端报告下载了 1 GB,但服务端日志显示只发了 800 MB,那中间一定有问题——可能是连接被中途重置,可能是客户端提前关闭了连接,也可能是日志记录有延迟。

我遇到过一次诡异的情况:客户端显示下载完成、速度也很正常,但校验和对不上。翻服务端日志才发现,请求被处理了两次,一次正常返回 1 GB,另一次在传输到 300 MB 时被客户端断开。原因是客户端脚本里有重试逻辑,第一次请求因为超时被判定失败后触发了重试,但第一次的连接没有真正断掉,两边都在跑。这种问题只看客户端是永远查不出来的,必须两端对账。

4. 客户端怎么测:命令行、脚本、浏览器三条路

准备工作做完,终于到正题了。客户端这边有三条路可以走,各有各的适用场景:命令行工具适合快速验证和自动化,自写脚本适合需要精细控制和详细记录的场合,浏览器适合模拟真实用户的行为但可控性最差。我一般先用命令行确认链路基本正常,再用脚本做正式的批量测试,浏览器只在需要验证前端下载体验时才用。

4.1 命令行一行命令拿到全套时间

命令行下载工具里有个参数可以直接输出各个阶段的时间,这是我最常用的手段,一条命令把连接耗时、首字节时间、总耗时、平均速率全拿到了:

curl -o /dev/null -s \ -w "DNS:%{time_namelookup}s 建连:%{time_connect}s 首字节:%{time_starttransfer}s 总耗时:%{time_total}s 平均速率:%{speed_download}B/s 总字节:%{size_download}\n" \ http://192.168.1.20:8000/test-1g.bin

几个参数的含义值得说清楚。-o /dev/null是丢弃下载的数据不落盘,这样测出来的就是纯网络吞吐,不受磁盘影响——这就是前面说的"分清洗网络还是测磁盘"的具体手段。-s关掉进度条,让输出干净,方便后面用脚本解析。-w后面的格式串就是你要的指标。

各字段对照如下:

字段含义异常时的排查方向
time_namelookup域名解析耗时内网测试建议直接用 IP,排除解析干扰
time_connectTCP 握手完成耗时偏高说明链路延迟大或有中间设备
time_starttransfer收到首字节耗时偏高说明服务端处理慢
time_total整个请求耗时结合文件大小算吞吐
speed_download平均下载速率(字节/秒)注意单位,除以 1048576 得 MiB/s
size_download实际收到字节数与文件大小对不上说明传输被中断

把-o /dev/null换成真实文件路径,同样的命令就变成了端到端的磁盘写入测试,两次结果一对比,瓶颈在哪清清楚楚。这个对比手法我已经用了不知道多少次,几乎是条件反射。

还有两个参数在排查时特别好用:--limit-rate 2M可以主动限速,用来验证限速策略是否生效,或者模拟弱网环境看客户端的表现;-C -是断点续传,配合-r 0-1048575指定字节范围,可以验证服务端是否正确支持分段请求。分段请求支持得好不好,直接影响到大文件下载在中断后能不能续上,这个能力在真实环境里非常关键。

4.2 Python 流式下载脚本(可复用)

命令行够快,但要做多轮测试、要记录瞬时速率、要做落盘时间的精确统计,就得自己写脚本。下面这段我一直在用,逻辑很朴素,但它同时做了四件事:分块下载、实时打印瞬时和平均速率、计算校验和、把落盘时间算进总耗时。

import os import sys import time import hashlib import requests URL = "http://192.168.1.20:8000/test-1g.bin" CHUNK = 1024 * 1024 # 1 MiB 一块 def download(url, out_path): sha = hashlib.sha256() total = 0 start = time.time() last_t = start last_bytes = 0 with requests.get(url, stream=True, timeout=10) as r: r.raise_for_status() size = int(r.headers.get("Content-Length", 0)) with open(out_path, "wb") as f: for chunk in r.iter_content(chunk_size=CHUNK): if not chunk: continue f.write(chunk) sha.update(chunk) total += len(chunk) now = time.time() if now - last_t >= 1.0: inst = (total - last_bytes) / (now - last_t) / 1048576 avg = total / (now - start) / 1048576 pct = total / size * 100 if size else 0 print(f"\r{pct:5.1f}% 瞬时 {inst:7.2f} MiB/s 平均 {avg:7.2f} MiB/s", end="") last_t, last_bytes = now, total # 关键:把缓冲区数据真正刷到磁盘,再计时 f.flush() os.fsync(f.fileno()) cost = time.time() - start print() print(f"总计 {total / 1048576:.1f} MiB,耗时 {cost:.2f}s,平均 {total / cost / 1048576:.2f} MiB/s") print(f"sha256: {sha.hexdigest()}") if __name__ == "__main__": out = sys.argv[1] if len(sys.argv) > 1 else "out.bin" download(URL, out)

这段代码里最值得说的是f.flush()加os.fsync()那两行。Python 写文件默认走用户态缓冲,f.write()返回的时候数据可能还在内存里没进内核,更没落盘。如果不做这一步,你测出来的耗时是"数据写完缓冲区"的时间,看起来飞快,但实际落盘可能还在后台慢慢进行,最后机器一掉电就丢数据。加上 fsync 之后,计时才真实反映"数据安全落到磁盘"需要多久。这两个调用的代价不小,在慢速磁盘上可能占掉总耗时的两三成,但这才是真实的端到端体验。

另外stream=True必须加。不加的话整个文件会被读进内存再返回,1 GB 文件就吃 1 GB 内存,测试机上跑几个并发直接把内存打爆了。分块大小选 1 MiB 是个折中,太小了系统调用次数多开销大,太大了单块处理时间长、进度刷新不流畅。

4.3 浏览器下载的坑

用浏览器测下载,最大的问题是它不告诉你细节。你只能看到进度条和最终那个速度数字,看不到首字节时间,看不到瞬时抖动,也不知道它有没有走缓存。而且浏览器对同一个 URL 的重复下载有很强的缓存倾向,第二次下载同一个文件可能直接从本地拿,速度显示得非常好看——这个数字跟你测的东西一点关系都没有。

所以我用浏览器测试的时候一定会做两件事。第一件是在 URL 后面挂一个随机参数,比如?t=1735689600,每次都不一样,强制绕过缓存。第二件是打开开发者工具的网络面板,看那条请求的详细时间分解,包括排队、等待、内容下载各占多少,这比进度条上的单一数字有用得多。

浏览器测试真正有价值的场景是验证前端行为:下载按钮点击后的响应速度、大文件下载时页面是否卡顿、下载中断后能否续传、多个文件同时下载时浏览器会不会限制并发数。这些是命令行工具测不出来的,因为它们跟浏览器的实现策略强相关。要做端到端体验评估,浏览器不可替代;要做链路性能测量,别用浏览器。

5. 数字不对劲时,按这个顺序排查

测试跑起来了,数字也出来了,但结果不符合预期——这是常态,不是意外。我做过的大部分测试第一轮结果都是"不对劲"的。关键是要有一套固定的排查顺序,从最容易排除的原因开始,一步步缩小范围,而不是东一榔头西一棒子地试。下面这个顺序是我踩了无数坑之后固化下来的,基本能覆盖八成的异常情况。

5.1 先排除回环,再排除磁盘

第一步永远是回环测试。在服务端本机上用127.0.0.1下载同一个文件,这一步把网络完全排除掉了,测的只剩服务端自身和磁盘。如果回环速度就很低,那问题一定在服务端或磁盘,跟网络没关系,后面的网络排查全部可以跳过。

回环正常(比如能跑到磁盘的顺序读上限),跨机就变慢,那问题在网络侧。这时候再细分:把下载目标指向空设备,如果速度快了,说明瓶颈是跨机写入时的磁盘;如果还是慢,说明是链路或者协议栈的问题。

这个"二分"的思路看似简单,但它能把一个模糊的"慢"拆解成明确的几个可能方向。我见过太多人一上来就怀疑网线、怀疑交换机,折腾半天最后发现是目标盘的剩余空间不足导致的写入降速。按顺序排查,五分钟就能定位。

5.2 缓存是最常见的假象

速度异常地快,比理论峰值还快,那几乎可以肯定是缓存导致的。可能是客户端缓存、可能是服务端内存缓存、也可能是中间某个环节的缓存。判断方法很简单:换一个从没被访问过的新文件再测一次,如果速度掉下来了,说明之前那次是缓存命中。

服务端侧的热缓存影响特别大。一个 1 GB 的文件被读过一次之后,如果机器内存充足,内核会把它整个留在页缓存里,后续的读取完全不碰磁盘,速度能翻好几倍。这在测试里不算错误,但你必须知道自己测的是"内存到网卡"的速度,不是"磁盘到网卡"的速度。做存储性能验收时如果没意识到这一点,很容易得出过于乐观的结论。

客户端侧的缓存相对好办,每次换文件名或者在 URL 上挂随机参数就能绕开。麻烦的是中间环节的缓存,你看不见也控制不了,只能通过在请求头里加禁用缓存的字段来尝试绕过,或者干脆换一个全新的文件名重新上传一份——用没被访问过的东西测,永远是排除缓存最可靠的办法。

5.3 并发多流与单流的对比

单流跑不满带宽是极其常见的现象,尤其在万兆环境和长距离链路上。单条 TCP 连接的吞吐受窗口大小和往返延迟的乘积限制,延迟越大、窗口越小,单流能跑到的上限就越低。这不是故障,是协议本身的特性。

判断方法是起多个并发下载,看总吞吐能不能超过单流。如果能,说明单流受限,链路本身没问题;如果不能,说明瓶颈在更底层,可能是链路带宽真的不够,也可能是某个共享资源(磁盘、CPU、中断)被打满了。

# 同时发起 8 个下载,把总吞吐加起来看 seq 1 8 | xargs -P 8 -I{} curl -o /dev/null -s \ -w "流{}: %{speed_download} B/s\n" \ "http://192.168.1.20:8000/test-1g.bin?t={}"

跑完把 8 个数字加起来,跟单流结果对比。我实测过的典型情况是:单流 300 MB/s,8 流合计能到 900 MB/s 以上。这种结果说明链路健康,只是单流受协议特性限制。如果你的业务场景本来就是多用户并发下载,那多流测试的数字才是有参考价值的;如果业务是单用户下载单个大文件,那单流数字才是你该关心的。测试方法要贴合真实使用场景,这一点比测出漂亮的数字重要得多。

5.4 一张对照表:症状、原因、验证手段

排查过程中最费时间的不是修,是确定原因。我把常见的异常症状整理成一张表,遇到问题时先对照着缩小范围,再去动手验证:

症状可能原因快速验证手段
速度远超理论峰值缓存命中或文件是空洞换新文件重测,ls -ls看占块
曲线规律性掉零磁盘写入跟不上,接收窗口被填满换空设备目标对比
开头快后面断崖下跌磁盘缓存耗尽(叠瓦盘典型)观察磁盘温度与写入队列
单流只有峰值的三分之一窗口与延迟的乘积限制起多流看总和
首字节时间特别长服务端处理逻辑串行用小文件单独测响应
校验和对不上内存故障或传输中被截断重复多次,看是否稳定复现
速度时快时慢无规律无线链路或省电模式降频换有线,关闭电源管理再测
下载到 99% 卡住服务端未正确返回结束标记对比服务端日志的实际发送字节

这张表不是万能的,但它能帮你快速排除掉一大半可能性。真正难查的是多个原因叠加的情况,比如磁盘慢加上链路抖,两个因素互相掩盖,单看哪一个都正常。这时候就得用变量控制法:一次只改一个条件,其他全部保持不变,逐项排除。

6. 从一次性测试到可重复的压测脚本

单次测试只能告诉你"此时此刻大概是这个数",但环境是会变的,负载是会涨的,设备是会老化的。真正有价值的做法是把它变成一个可以反复执行、结果可以横向对比的东西。这一步的工作量比前面所有加起来都大,但一旦搭好,后面每次验证都只需要跑一条命令、看一张表。

6.1 断点续传与限速:两个必须单独验证的能力

吞吐量只是下载能力的冰山一角。真实环境里更常见的问题是:网络中断之后能不能续上、服务端有没有做限速并且限速是否生效。这两项能力不验证,等真出问题的时候才发现不支持,代价就大了。

验证断点续传的方法是先下载一部分然后主动中断,再带着续传参数重新发起,看能不能从断点处接着下,而不是从头开始。判断依据是看服务端日志或者客户端收到的字节数——如果第二次请求只传输了剩余部分,说明续传生效;如果又从头传了一遍,说明服务端不支持分段请求,或者客户端没带对参数。有些服务端返回了支持续传的响应头,但实际执行的时候又从头开始传,这种"假支持"必须靠实际测试才能发现。

验证限速的方法是先确认不限速时的基准速度,然后加上限速参数再测一次,看实际速度是否落在设定值附近。这里要注意,限速通常只对大文件有意义,小文件还没跑到限速值就传完了,测不出来。另外限速的实现方式有的是在应用层做流量整形,有的是靠阻塞读取,前者平稳后者呈锯齿状,两种都算正常,不用纠结。

# 限速到 2 MiB/s,观察实际速率是否稳定在这个值附近 curl -o /dev/null -s --limit-rate 2M \ -w "实际速率: %{speed_download} B/s\n" \ http://192.168.1.20:8000/test-1g.bin

如果设定 2 MiB/s 实际跑出 5 MiB/s,说明限速参数没生效,可能是单位写错了(小写 m 和大写 M 差别巨大),也可能是被其他配置覆盖了。这种地方踩一次坑,后面就再也不会错了。

6.2 多轮取中位数,别拿一次结果当结论

单次测量受各种随机因素影响,波动个 10% 到 20% 太正常了。拿一次结果当结论,很容易得出错误判断——尤其在做前后对比的时候,先测一次、改个配置再测一次,两次的差异可能完全来自波动而不是配置改动。

我的做法是每轮至少跑 5 次,去掉最高和最低,取中间三次的平均值;如果波动特别大(比如最高最低差 30% 以上),说明环境本身不稳定,这时候先别急着比较配置效果,得先把稳定性问题解决掉。不稳定环境下的任何对比结论都不可信。

跑多轮的时候有个小技巧:把结果直接输出成 CSV 格式,最后用表格工具一汇总,趋势一目了然。

#!/bin/bash URL="http://192.168.1.20:8000/test-1g.bin" echo "轮次,时间,DNS,建连,首字节,总耗时,平均速率MiB" > result.csv for i in $(seq 1 5); do out=$(curl -o /dev/null -s \ -w "%{time_namelookup},%{time_connect},%{time_starttransfer},%{time_total},%{speed_download}" \ "$URL?t=$RANDOM$i") speed=$(echo "$out" | awk -F, '{printf "%.2f", $5/1048576}') echo "$i,$(date +%H:%M:%S),$out" | awk -F, -v s="$speed" 'BEGIN{OFS=","}{print $1,$2,$3,$4,$5,$6,s}' >> result.csv sleep 2 done cat result.csv

?t=$RANDOM$i这个随机参数就是前面说的绕缓存手段,成本极低但效果显著。每次跑完sleep 2让链路缓一缓,避免上一轮的连接还没完全回收就开下一轮,那种情况下测出来的数字会偏慢。

6.3 记录测试条件,比记录结果更重要

我见过太多测试报告只写"下载速度 105 MB/s",然后这份报告三个月后就没法用了——因为没人记得当时用的是什么文件、多大、目标盘是哪块、网络拓扑什么样、机器上还跑着别的东西没有。条件丢了,结果就是一堆孤立的数字。

所以我的测试记录里,结果永远放在后面,条件放在前面。至少要记清楚这些:测试文件的路径、大小、校验值;服务端和客户端各自的机器配置、磁盘型号、网卡速率;两端之间的网络路径(是不是同一台交换机、有没有跨网段);测试时机器上的其他负载情况;以及本次测试是冷读还是热读。这些信息看着琐碎,但当你三个月后要对比"为什么现在比当时慢了 15%"的时候,它们就是唯一能救命的东西。

还有一点来自实际教训:测试环境的时钟要同步。如果服务端和客户端时间差了几分钟,你对着两边的日志排查问题时会对不上,越查越糊涂。这个坑我踩过一次,查了整整一个下午,最后发现是两台机器的系统时间差了七分钟。

7. 几个容易翻车的边角情况

写了这么多,最后补几个零散但真实存在的坑,都是我在不同场景下栽过的,属于文档里不会写、但实际操作中很可能遇到的情况。

第一个是笔记本的电源管理。用笔记本做客户端测试时,如果没插电源,系统会自动降频,网卡也可能进入省电模式,测出来的速度比插着电源时低 20% 到 30% 是常事。做正式测试前先把电源插上、把电源计划调成高性能,这个动作只要五秒钟,但能避免一整轮的无效数据。

第二个是安全软件的实时扫描。有些安全软件会对下载的文件做实时扫描,数据一落盘就触发检查,这会显著拖慢写入速度,而且表现得很隐蔽——你看磁盘占用不高,CPU 占用也不高,但速度就是上不去。如果怀疑是这个原因,把测试目录加到排除列表里再测一次就能确认。测试完记得把排除项删掉。

第三个是文件系统本身的开销。同一个磁盘,不同的文件系统、不同的挂载参数,写入性能能差不少。比如某些文件系统默认开启写屏障和数据校验,安全性提高了,但写入速度会明显下降。这些不是故障,是设计取舍。做测试时如果发现速度比预期低一截,看一眼挂载参数,很可能答案就在那里。

第四个是临时目录的特殊性。很多人图方便把测试文件放在临时目录里,但临时目录经常被挂载成内存文件系统,数据根本不落磁盘。在这种目录里做写入测试,测出来的速度就是内存的速度,能跑到好几个 GB/s,漂亮但完全不代表真实存储能力。测试文件一定要放在目标磁盘的常规目录下。

第五个是长文件的碎片化。反复创建、删除大文件会让文件系统产生碎片,后续创建的大文件可能是碎片化的,读取时需要更多寻道,顺序读速度会下降。测试机上养成好习惯,用完就清理干净,不要堆积一堆测试文件。

这套东西说到底没什么高深技术,全是笨办法堆出来的:造一个真实的大文件、起一个可控的服务、用两种方式各测一遍对比、遇到异常按顺序二分排查、多轮测量取中位数、把条件记录清楚。每一步都简单到不需要解释,但组合起来就能把"这个下载到底有多快、为什么是这个速度"这件事说清楚。真正难的不是操作,而是忍住不用一个平均值糊弄过去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询