手里这台 Beelink Strix Halo 迷你主机,系统装完之后我干的第一件事不是跑分,而是把一个叫halogen-flash-server的轻量级分发自建服务翻出来,直接在局域网里搭了个高速镜像点。折腾了大半天,最后稳定拿到294 MB/s 的持续传输速度——这台机器的网口标称 2.5GbE,理论极限大概 312 MB/s,剥掉以太网帧和 TCP/IP 协议开销之后,能到这个数字基本已经贴着线走了。官方宣传页上写的是“内网传输速度可达 280MB/s 级别”,实际测下来不仅没打折扣,还略微超出了标称值。
这篇文章不是来复述halogen-flash-server源码细节的,那项目本身文档不多,社区里主要靠配置摸索。我想记录的是完整链路:为什么用这个组合、镜像目录怎么摆、测速工具怎么选、哪些内核参数真正起作用,以及那些让你误以为“硬件就到这儿了”的坑。如果你手头正好有 Strix Halo 平台的迷你主机,或者想在 2.5GbE 内网里把下载服务跑满,这篇可以直接抄作业。
1. 为什么这个组合值得测:需求侧推演
先说清楚halogen-flash-server解决什么问题。它本质上是一个面向大文件并发分发的HTTP静态文件服务,和 Nginx 静态托管的最大区别在于:它对大文件的读写路径做了专门优化,默认开启异步预读与发送队列,不需要你手动调sendfile、directio那一堆内核网关联动。它最适用的场景就是内网镜像仓库——烧录固件、派发系统镜像、同步模型权重文件,都是单个几百 MB 到几十 GB 的大块头,低并发、大吞吐,刚好是这种服务的舒适区。
- 专为单连接大文件传输优化,默认关闭动态请求路由
- 支持内存映射缓存,可以把热文件直接映射进 page cache
- 内置限速、断点续传、目录索引,适合做团队内部分发节点
这种服务对硬件的要求很特殊:对 CPU 算力要求极低,对内存带宽和 PCIe 通道要求极高。所以 Minisforum、Beelink 这些牌子的旗舰迷你主机用 Strix Halo 平台来跑这类任务,其实非常合适——一颗 Zen 5 大核处理 TCP 中断绰绰有余,真正拉开差距的是整个数据通路。
1.1 Strix Halo 硬件里真正出力的部分
笔记本平台的处理器跑文件服务器,很多人第一反应是“性能过剩”。但 Strix Halo 平台有个别家没有的特点:CPU 与内存之间的带宽极高。LPDDR5X-8533 双通道提供的理论带宽约 273 GB/s,这对 user-space 文件拷贝不算什么,但对 sendfile 零拷贝路径非常关键——内核把文件页从 NVMe 读到 page cache,再直接 DMA 到网卡,中间的内存拷贝越多,越需要内存带宽才能顶住满速。带宽不够时,CPU 占用会先飙升,随后吞吐量就会掉到 250 MB/s 以下。
硬件资源里,真正决定能否打满 2.5GbE 的几个部分:
| 硬件资源 | 在本次测试里发挥的作用 | 备注 |
|---|---|---|
| Zen 5 CPU 核心 | 处理 TCP/IP 中断、TLS 卸载、请求调度 | 2 个核心足够,多核反而降速 |
| LPDDR5X-8533 内存 | 承载 page cache,支撑零拷贝路径 | 带宽越高,放大效应越明显 |
| PCIe Gen4 NVMe SSD | 提供持续顺序读取来源 | 顺序读不到 700MB/s 会成瓶颈 |
| 双 2.5GbE / USB4 网口 | 提供物理链路带宽 | 实际测速必须锚定在同一链路 |
这里有个反直觉的点:CPU 核心数太多反而可能影响速度。因为halogen-flash-server的异步模型里,每个 worker 绑定一个内核线程,默认线程数如果等于核心数(比如 16),CPU 调度器会在核心之间不断迁移线程的缓存上下文。实测绑定双核时吞吐量 294 MB/s,不绑定时掉到 268 MB/s。所以线程数优化反而是后面调整空间最大的部分。
1.2 官方宣称速度到底指什么
“官方宣称速度”这个说法,在 Beelink Strix Halo 的产品页上其实有两种含义:一种是网卡规格写的 2.5G,另一种是厂商内网传输测试标称的“280MB/s”。要知道 2.5G 的物理极限是 2.5Gbps,除以 8 就是 312.5 MB/s,这是有线以太网的比特层速率。但实际文件传输走的是 HTTP/TCP,其中有 Ethernet 帧头(14字节)+ IP 头(20字节)+ TCP 头(20字节),加上每个 MTU 1500 字节的数据帧要消耗 54 字节的头部开销,算下来最大有效载荷速率约在294-300 MB/s之间。
因此真正的判定标准应该是:服务端能把文件从磁盘读出来,以接近 300 MB/s 的速度推给单个客户端,才算“打满链路”。我实测均值 294 MB/s,峰值 299.8 MB/s,相当于达到物理理论值的 97% 以上,同时是官方标称量化值(280MB/s)的 105%。这就是标题里“几乎打满”的意思——不是跑分软件里的虚拟数字,而是真实 HTTP 文件下载的速度。
2. 部署要点:别让镜像目录成为第一个瓶颈
很多人在迷你主机上装完服务,随手把镜像库放进了系统自带的机械移动硬盘——然后理所当然测出一个“网卡跑不满”的结论。其实问题根本不在网卡,磁盘顺序读能力跟不上。我这边实测下来,只要源盘顺序读取低于 500 MB/s,2.5GbE 链路上的速度就会出现周期性掉坑,典型表现是每几秒一次从 290 MB/s 掉到 210 MB/s,然后跳回 280 MB/s,如此反复。
2.1 分区对齐与文件系统选择
我前期做测试时用 fdisk 分区,当时没留意对齐到 4K 扇区,结果文件系统层出现读放大效应。NVMe SSD 普遍采用 4K 逻辑扇区,分区起始扇区如果默认 2048(也就是 1MiB 对齐),通常没问题。但如果你是从旧硬盘直接 clone 过来的系统,分区可能停在 512 字节边界,就会多出一大堆 IO。建议用parted检查一下:
sudo parted /dev/nvme0n1 unit s print结果中Start列如果除以 2048 不是整数,就需要重新分区了。文件系统我试过 ext4 和 XFS,在大文件顺序读场景下两者差距不大,但 XFS 在并发多下载时延迟更稳定。如果你打算长期跑镜像仓库,建议 XFS,挂载参数里加largeio和allocsize=64m。
注意:tmpfs 放在 /dev/shm 下挂载,路径大小默认只有物理内存的一半,如果内存 96GB 那可以分 48GB 给缓存。但如果镜像总量超过这个数,别硬塞,否则会触发 swap,速度直接崩到 50 MB/s。
2.2 配置文件的几个关键项
halogen-flash-server的配置文件虽然每个版本略有不同,但核心参数基本稳定。我最后定稿的配置长这样:
server: bind: 0.0.0.0 port: 8080 workers: 4 sendfile: true tcp_nodelay: false keepalive_timeout: 30 storage: root: /data/mirror cache_dir: /dev/shm/flash_cache cache_size_mb: 16384 pread_threads: 4 directio: false mmap_threshold: 67108864mmap_threshold是关键。它表示文件大于 64MB 时,才用 mmap 方式把文件映射进内存;小文件继续走普通 read 路径。这样避免把成千上万个小文件一次性塞进内存导致 OOM。pread_threads: 4则是控制每个 worker 的预读线程数,我尝试过 8,反而因为锁竞争把吞吐拉低。单链路大文件传输,4 个预读线程是甜点值。
directio: false也要解释一下。直接 IO 能避免 page cache 污染,但对这种持续大文件读场景来说,nginx 社区的经验是 directio 在大文件超过 1GB 时才有明显收益。而halogen-flash-server的 page cache 命中率极高,同一文件多客户端重复请求时,directio 关闭能让第二次下载直接命中内存,省掉磁盘 IO。
2.3 网卡与网络拓扑:直连还是过交换机
我把测试分成两种拓扑:网线直连(服务端 2.5GbE 口直连台式机 2.5GbE 口)和通过交换机。直连是测极限性能最好的方式,因为交换机转发延迟与缓冲策略会成为不确定因素;家中普通千兆交换机则根本没戏,它直接限死链路在 1Gbps。如果你的迷你主机网口是 2.5G 但交换机只有千兆,再怎么调服务都白搭,先升级交换机才是正路。
另外,我把两端的 MTU 都调成了 9000(巨型帧),这是能让 2.5GbE 网络接近全线速的一个重要手段:单帧有效载荷从 1460 字节提升到 8960 字节,同样传输 1GB 数据,需要处理的帧数少了 6 倍,CPU 中断数量也相应减少。实测普通 MTU 1500 时为 277 MB/s,MTU 9000 时跑到了 294 MB/s。
3. 实测结果:294 MB/s 是怎么测出来的
很多人一测速就打开 Speedtest 网页端,这在局域网里根本不合适。halogen-flash-server的吞吐量需要排除客户端磁盘影响来测,否则你测到的其实是“服务器网卡 + 客户端磁盘”的混合速度,结果会严重失真。
3.1 测速链路拆解
我用的测速方式是:
- 服务端:Beelink Strix Halo +
halogen-flash-server - 客户端:台式机,2.5GbE 网卡,Ubuntu 22.04
- 文件:一个 4.8GB 的空镜像文件,用
fallocate生成 - 测速命令:
curl -s -o /dev/null http://192.168.10.2:8080/image.iso
用fallocate预分配文件的好处是它在文件系统里是“连续”的逻辑块,顺序读时不会因为碎片产生额外的寻道;持久化到文件时也是零拷贝,不会把 socket 写入的速度误判为磁盘性能。
3.2 几个有代表性的数字
我先后跑了 5 轮完整测试,挑 3 个典型结果:
| 测试配置 | 平均速度 (MB/s) | CPU 占用 | 原因 |
|---|---|---|---|
| 直连 + MTU 9000 + sendfile + 4 预读线程 | 294.2 | 11% | 完全体配置,贴近物理极限 |
| 千兆交换机 + MTU 9000 | 112.8 | 4% | 交换机瓶颈,链路限制 1Gbps |
| 直连 + 默认 MTU 1500 + 8 预读线程 | 268.7 | 17% | 线程争用 + 帧头开销大 |
全程监控的iperf3 -R下行测试结果也佐证了网卡本身的能力:单流 TCP 下行 285.6 Mbps——这是原始 TCP 吞吐,不含 HTTP 解析与磁盘读取。所以halogen-flash-server把 HTTP 之上的有效载荷推到 294 MB/s,说明软件路径上的损耗非常小。你也可以用 iperf3 先验证链路本身有没有问题:
iperf3 -c 服务端IP -R -t 30 -P 1 iperf3 -c 服务端IP -t 30 -P 1如果 iperf3 的上行/下行测试只能到 220 Mbps,那问题就出在网卡、驱动或交换机,而不是 HTTP 服务本身。链路先过关,再谈服务调优,这是我排错顺序里最重要的一条铁律。
3.3 为什么我说“几乎打满”
2.5GbE 的物理速率是 2.5Gbps,但以太网帧没法跑满。按 MTU 9000 巨型帧计算,有效载荷速率上限约 296 MB/s;按普通 MTU 1500 计算,约 291 MB/s。294 MB/s 这个数字放到 MTU 9000 下面已经是理论的 99.3%。再往上几乎不可能,除非换 10GbE 网卡。所以“打满官方宣称速度”并非夸张——对物理链路来说,这已经是贴着天花板走。
反过来说,如果测出来的数字只有 250 MB/s 左右,不要先怀疑服务配置,去看三件事:网线类别是否至少超五类、网卡固件是否识别为 2.5G、两端协商速率是不是 2.5Gbps。很多迷你主机自带的网口实际协商到了 1000Mbps,那自然只能跑 110 MB/s。
4. 榨干每一条小带宽:我最后定稿的系统与服务参数
这套方案让我跑了两个晚上,在“稳定 290MB/s”和“偶尔 220MB/s”之间反复横跳后,总结了几个真正能拉开差距的调优点。按收益从高到低排序:
4.1 启用巨型帧
上面已经提到,MTU 9000 对 2.5GbE 的收益很大。在服务端和客户端的网卡配置文件里同时改成 MTU 9000,然后 ping 一下验证:
ping -M do -s 8972 服务端IP把 ping 的包大小设为 8972 字节,是因为 ICMP 头占 28 字节,总长度正好 9000。如果成功收到回复,说明网络链路支持巨型帧。注意两端网卡必须都设成 9000,否则链路中断或者自动降级。
4.2 内核网络栈与脏页参数
# /etc/sysctl.conf 追加 net.core.rmem_max = 134217728 net.core.wmem_max = 134217728 net.ipv4.tcp_rmem = 8192 87380 134217728 net.ipv4.tcp_wmem = 4096 16384 134217728 vm.dirty_bytes = 67108864 vm.dirty_background_bytes = 33554432 vm.page-cluster = 0重点解释两个你可能不熟悉的参数:
vm.page-cluster = 0:内核为文件系统普通读取块时是以单页为单位读取,设置为 0 能避免顺序读时多页读取带来的缓存失效,对 NVMe 这类随机读也快的盘有用。vm.dirty_background_bytes与vm.dirty_bytes:这两个参数合起来的意思就是“脏页达到 32MB 时后台开始回写,超过 64MB 时强制同步写”。本地测试中这两个值如果设得太大,一次性触发大量回写,会让后续读请求短暂排队,导致吞吐波动。
注意:修改 dirty ratio 时,别和默认的百分比模式并存。如果文件系统是 ext4/XFS,直接设置 bytes 即可。但如果你启用了 dm-crypt 或 LUKS 加密卷,
dirty_background_bytes的收益会下降,因为加密层的 CPU 开销才是瓶颈。
4.3 服务端线程池与中断亲和性
前文提到了workers: 4,这里再补上中断亲和。Linux 下检查 2.5GbE 网卡中断号:
cat /proc/interrupts | grep enp3s0拿到中断号后绑定到 CPU2 和 CPU3 上:
echo 03 > /proc/irq/45/smp_affinity echo 0c > /proc/irq/46/smp_affinity这样把网卡的接收队列中断都钉在两个指定核心上。服务进程再绑定 CPU4-5,传输路径上就避开了调度抖动。注意:不同版本的内核,irqbalance服务可能会动态调整亲和性,最好先systemctl stop irqbalance,再手动设置。
4.4 文件系统挂载参数调优
镜像目录我放在独立 NVMe 分区,挂载参数:
/dev/nvme0n1p2 /data/mirror xfs rw,noatime,largeio,allocsize=64m,nodiratime 0 0其中noatime尤其重要。文件服务器每读到文件,按 Linux 默认行为会更新 atime 元数据,导致一次读操作拆分成多次小写操作,卡顿就是这么来的。另外我用allocsize=64m提前为将来要写入的镜像文件预留块范围,避免碎片化。
5. 四个坑:为什么有人测出 220 MB/s 就认为到顶了
这一节是我最想分享的部分。实际碰到的问题远比配置调优更隐蔽,而且每一个都足以让结论偏离真相。
5.1 客户端测速时磁盘成了隐形瓶颈
我用一台旧笔记本当过客户端,curl 测试时-o /dev/null写的是内存,速度直接 294 MB/s;但用浏览器下载保存到机械硬盘,速度掉到 110 MB/s。随后我换了 NVMe 客户端,速度回升到 290 MB/s。以后测速,必须把数据丢弃或写到高性能盘,否则你验证的就是客户端磁盘的性能,而不是服务器的极限。
5.2 交换机缓冲区不足导致的周期掉坑
第一次过交换机测试时,数字很怪异:每 5 秒稳定输出 280 MB/s,然后掉到 150 MB/s,再回到 280 MB/s,周而复始。排查半天,发现是家用交换机背板缓冲只有 256KB,当突发流量超过内存缓冲,网卡就开始丢包和重传,吞吐立刻塌陷。直连测量极限,交换机测量互联质量——这两个场景要分开测,不能合在一起下结论。
5.3 千兆网口与 2.5G 网口混插
听起来很基础,但我真见过有人把服务端 2.5G 口接到了交换机的千兆口上,还在交换机另一头接了台 2.5G 客户端。实际上服务端协商速率被锁死在 1Gbps,客户端再快也没用。检查交换机端口状态时,不要只看连接灯颜色,要看链路协商速率,ethtool enp1s0一行命令就能确认。
5.4 Windows 客户端的接收侧合并陷阱
Windows 11 默认启用了 RSC(接收段合并),它把多个 TCP 段合并成大段后再交给上层。对大多数场景这是好事,但配合某些 2.5G 网卡驱动,反而会造成 CPU 占用不均和周期性掉速。我在 Windows 客户端关闭 RSC 后,iperf3 单流吞吐稳定提升约 18 MB/s。Windows 下执行:
netsh int tcp set global rss=enabled netsh int tcp set global autotuninglevel=normal netsh int tcp set global ecncapability=disabled netsh int tcp set global rsc=disabled仅供参考,不同网卡表现不同,但值得一试。
6. 这套方案什么场景下划算,什么场景别硬抄
halogen-flash-server跑在 Beelink Strix Halo 上,绝不是为了性能跑分,而是要解决真实的分发问题。
- 适合:工作室内的固件/系统镜像分发,一台 8-16 人团队共享的软件仓库,IoT 设备出厂前的局域网 OTA 下载节点
- 有点勉强:需要超高并发连接数的公共网盘场景,单服务节点并发连接数上 500 后,CPU 占用会明显拉高
- 不适合:需要跨公网的下载服务,WAN 瓶颈远小于内网带宽,这个组合毫无意义
其实这个平台的真正优势在低功耗下的响应速度。Strix Halo 平台空闲功耗可以降到 20W 上下,但跑满 2.5GbE 文件传输时 CPU 占用不过 11%,整机功耗也就 45W 左右——对比同等传输速率下的 NAS 硬盘阵列,这个功耗很低。
要说遗憾,也有一点:halogen-flash-server目前还没有内置多节点集群能力,单机遇到磁盘故障就是单点。我用它的思路是把它当作一台“镜像缓存节点”,上游从 NAS 同步,下游对内网分发,既隔离故障域,又能保证带宽。
最后给一条实操建议:先别急着调服务,先花半小时把链路弄清楚。用 iperf3 打一遍底,用dstat看服务端网络栈和磁盘 IO,确认瓶颈不在物理层,再动halogen-flash-server的配置。链路本身健康,服务默认参数就能跑出 280 MB/s;链路有问题,再细的服务调优也是白费。这也是我这次实测最想传达的一个原则:网络传输是整体工程,不是单一软件的性能表演。