资讯动态

OpenFreeMap 性能基准测试全解析:基于 Btrfs 硬链接方案的 wrk 压测实战与结果解读

发布时间:2026/9/17 8:25:03 来源:尧图企业网站定制
OpenFreeMap 性能基准测试全解析基于 Btrfs 硬链接方案的 wrk 压测实战与结果解读【免费下载链接】openfreemapFree and open-source map hosting solution with custom styles for websites and apps, using OpenStreetMap data项目地址: https://gitcode.com/GitHub_Trending/op/openfreemapOpenFreeMap 是一个完全开源、可直接自托管的矢量瓦片托管方案其核心思路是用“Btrfs 分区镜像 300 百万个硬链接文件 优化过的 nginx 配置”替代传统 tile server让瓦片以纯文件系统方式被直接提供。本文基于仓库 docs/benchmark/results.md 记录的两次真实压测Hetzner 专用服务器与 BuyVM KVM 云主机结合 docs/benchmark/README.md、wrk_custom_list.lua 与 nginx_to_path_list.py 的源码级细节完整还原压测命令、数据生成方式与结果解读并给出你在自托管环境中复现该测试的完整步骤。读完本文你将理解为什么“没有 tile server”也能达到每秒 4.7 万次请求以及不同存储方案本地 NVMe SSD 对比远程 Block Storage对瓦片服务性能的真实影响。为什么要做基准测试从 tile server 到纯文件系统传统瓦片托管通常运行一个常驻的 tile server 进程来响应请求。而 OpenFreeMap 选择了完全不同的路线将 Planetiler 生成的 .mbtiles 通过仓库自带的 extract_mbtiles.py 脚本解包成一个包含约 3 亿个文件的 Btrfs 分区镜像再用 shrink_btrfs.py 收缩分区最终由优化过的 nginx 配置直接以静态文件方式提供服务。平均每个瓦片文件仅约 450 字节。这一设计把“运行中的服务”替换成了“文件系统层面上的实现”。正如项目 README.md 所述Linux 内核的文件缓存是性能最高、经过最充分测试的代码之一因此这种方案具备非常可观的性能潜力。要验证这个设计是否真的能在生产环境扛住真实流量就必须做基准测试——这就是 docs/benchmark 目录存在的意义。基准测试方法论基于真实访问日志回放的压测设计500k 真实请求路径回放与常见的随机 URL 压测不同OpenFreeMap 的压测使用真实世界的使用模式从服务器访问日志中提取约 50 万条真实瓦片请求路径逐条回放。这一点很关键因为真实用户行为通常是高度非均匀的——例如海洋区域的瓦片几乎无人访问。如果随机生成 URL压测结果会与实际负载严重脱节。路径列表由 nginx_to_path_list.py 从 nginx 的 JSONL 格式访问日志即access.jsonl生成其筛选逻辑如下import json with open(access.jsonl) as fp: json_lines fp.readlines() paths [] for i, line in enumerate(json_lines): log_data json.loads(line) if log_data[status] ! 200: continue if log_data[request_method] ! GET: continue uri log_data[uri] if tiles/ not in uri or not uri.endswith(.pbf): continue path log_data[uri].split(tiles/)[1] paths.append(path \n) with open(path_list.txt, w) as fp: fp.writelines(paths)从源码可以看到只有满足以下全部条件的日志条目才会被收录进压测路径列表响应状态码为200排除错误请求和重定向请求方法为GET压测目标是瓦片读取路径而非 POST 等写入类请求URI 包含tiles/且以.pbf结尾即只压测矢量瓦片请求排除其他资源。wrk 压测脚本循环回放与错误检测压测工具选用 wrk其自定义脚本 wrk_custom_list.lua 实现了请求路径的循环加载与逐条回放local counter 1 local lines {} local url_base /planet/fake_version/ -- trailing slash local path_list_txt /data/ofm/benchmark/path_list_500k.txt for line in io.lines(path_list_txt) do table.insert(lines, url_base .. line) end local function getNextUrl() local url_path lines[counter] counter counter 1 if counter #lines then counter 1 end return url_path end request function() path getNextUrl() local headers {} headers[Host] ofm return wrk.format(GET, path, headers, nil) end response function(status) if status ~ 200 then print(Non-200 response) print(Status: , status) print(Request path: , path) -- only works in single threaded mode (-t1) end end该脚本的几个要点所有请求都指向/planet/fake_version/前缀下的瓦片路径并设置Host: ofm头确保命中测试域名对应的 nginx server 块50 万条路径循环取用压测时长内可覆盖多轮完整回放响应回调会打印任何非 200 的状态码及对应路径用于确认压测期间没有产生错误响应注意打印请求路径仅在单线程-t1模式下可靠。压测执行注意事项README.md 明确强调了两条重要规则压测必须在localhost上运行而不是通过公网否则测出来的只是你的网络速度而不是服务器性能线程数选择上-t1时 URL 列表严格按顺序加载、结果更精确-t4则更接近真实世界的并发使用模式。标准压测命令格式为wrk -c10 -t4 -d10s -s /data/ofm/benchmark/wrk_custom_list.lua http://localhost其中-c10表示保持 10 个并发连接-t4表示使用 4 个线程-d10s表示压测持续 10 秒-s指定自定义 Lua 脚本。测试环境一Hetzner 专用服务器本地 NVMe SSD原文档记录的第一组测试运行在一台 Hetzner 专用服务器上配备 NVMe SSD。测试前先执行service nginx restart以清空 nginx 重启后的缓存确保结果是冷缓存下的真实表现。localhost 回环压测wrk -c10 -t4 -d60s -s /data/ofm/benchmark/wrk_custom_list.lua http://localhost Running 1m test http://localhost 4 threads and 10 connections Thread Stats Avg Stdev Max /- Stdev Latency 2.02ms 7.04ms 50.43ms 93.23% Req/Sec 8.42k 2.01k 18.52k 69.79% 2871265 requests in 1.00m, 230.65GB read Requests/sec: 47811.00 Transfer/sec: 3.84GB结果解读吞吐量达到每秒 47811 次请求一分钟内处理了 287 万次请求平均延迟仅 2.02ms最大请求时间 50.43ms且 93.23% 的请求延迟分布集中在平均值附近抖动很小读取数据量达 230.65GB传输速率 3.84GB/s全程没有任何错误无非 200 响应、无 socket error。原文档的评价是“Super much overkill”性能严重过剩要打满一条千兆Gigabit连接理论带宽只需约 125 MB/s而这里实测达到 3840 MB/s是需求量的 30 倍以上。最大请求时间表现也非常出色且压测全程零错误。跨网络压测受限于千兆带宽wrk -c10 -t4 -d60s -s /data/ofm/benchmark/wrk_custom_list.lua http://x.x.x.x Running 1m test http://144.76.168.195 4 threads and 10 connections Thread Stats Avg Stdev Max /- Stdev Latency 7.57ms 6.61ms 45.34ms 84.32% Req/Sec 293.85 141.33 1.18k 73.07% 71628 requests in 1.00m, 6.05GB read Requests/sec: 1191.88 Transfer/sec: 103.01MB通过真实网络对 144.76.168.195 这一公网地址压测时吞吐量骤降至每秒 1191.88 次请求、传输速率 103.01MB/s。原文档明确指出这才是在千兆网络条件下现实可达的上限。也就是说服务器本身的处理能力远高于网络链路能承载的量瓶颈在网络上而非瓦片服务本身。这个对比也恰好印证了 README 中“不要跨网络压测否则只是在测你的网速”的忠告。测试环境二BuyVM KVM 1TB Block Storage Slab第二组测试针对一个完全不同的存储架构BuyVM 的 KVM 虚拟机搭配 1TB 的 Block Storage Slab。该产品宣称使用 40Gbit 的 InfiniBand RDMA 存储网络可提供接近本地磁盘的性能。而实测结果冷缓存如下wrk -c10 -t4 -d60s -s /data/ofm/benchmark/wrk_custom_list.lua http://localhost Running 1m test http://localhost 4 threads and 10 connections Thread Stats Avg Stdev Max /- Stdev Latency 226.10ms 343.52ms 1.99s 87.75% Req/Sec 29.77 38.06 272.00 89.72% 3655 requests in 1.00m, 232.76MB read Socket errors: connect 0, read 0, write 0, timeout 8 Requests/sec: 60.87 Transfer/sec: 3.88MB关键数据吞吐量仅 60.87 次请求/秒与 Hetzner 的 47811 次/秒相差约 785 倍平均延迟高达 226.10ms最大延迟 1.99s出现了 8 次 timeout 类型的 socket 错误说明在冷缓存下网络存储的响应时延已经突破了 wrk 的等待阈值。即便反复测试并让缓存热起来性能虽有提升但仍远未达到千兆水平Requests/sec: 266.99 Transfer/sec: 23.07MB热缓存下提升到每秒 266.99 次请求、23.07MB/s但这与“接近本地磁盘性能”的营销宣传相去甚远。原文档最终给出的结论是放弃使用 BuyVM 的想法——尽管其在美国同价位段提供相当罕见的无限带宽但远程 Block Storage 的随机小文件读取性能不足以支撑面向 3 亿个小瓦片文件的瓦片托管场景。值得注意的是这个结论有一个明确的适用前提测试的是“平均 450 字节的小文件、随机访问”这一瓦片服务典型负载如果你的业务是顺序大文件读取结果可能会不同不应将本结论无差别推广。结果对比与结论测试环境存储类型吞吐量 (req/s)传输速率平均延迟错误Hetzner localhost本地 NVMe SSD47811.003.84GB/s2.02ms0Hetzner 跨网络本地 NVMe SSD1191.88103.01MB/s7.57ms0BuyVM 冷缓存远程 Block Storage60.873.88MB/s226.10ms8 timeoutBuyVM 热缓存远程 Block Storage266.9923.07MB/s——从这组对比中可以提炼出对 OpenFreeMap 架构的三点关键认知本地磁盘 内核文件缓存是性能的根基。Btrfs 镜像挂载后nginx 通过try_files直接命中文件系统当文件进入内核 page cache 后每次请求本质上是一次内存读取。这正是“没有 tile server”方案能跑出 47k req/s 的原因。项目 README.md 也提到该方案在冷 nginx 缓存下甚至能在回环接口上达到 30 Gbit 的吞吐能力。小文件随机访问对存储介质极其敏感。3 亿个平均 450 字节的小文件访问模式高度随机对磁盘 IOPS 和时延要求苛刻。远程网络存储即使宣传带宽很高其随机小文件访问的时延也无法与本地 NVMe 相比直接反映为 226ms 的平均延迟和 60 req/s 的吞吐。网络往往是真正的瓶颈。Hetzner 机器在千兆网络下只能跑出 103MB/s约 1.2k req/s与回环接口的 3.84GB/s 相差 37 倍。这解释了为什么生产环境只需很小的服务器配置即可支撑可观的真实流量——例如官方自托管文档 docs/self_hosting.md 中http-host 模块仅要求 300GB 磁盘空间SSD 为推荐而非必需无 SSD 时首次下载与解压会慢但日常服务对磁盘的依赖会因缓存而大幅降低。如何在你的自托管环境复现这套基准测试如果你已按 docs/self_hosting.md 部署了 OpenFreeMap 的 http-host 模块可以按以下步骤在本地复现压测准备路径列表将 nginx 访问日志转换为 JSONL 格式对应脚本读取的access.jsonl运行 nginx_to_path_list.py 生成path_list.txt。仓库中的 nginx 访问日志本身就是 JSONL 格式见 roundrobin.conf 中的access_log ... access_json buffer128k配置。注意路径文件不随仓库分发需要从你自己的日志生成。若日志量不足 50 万条可适当放宽筛选条件或用多天日志合并。部署 wrk 与 Lua 脚本将 wrk_custom_list.lua 放到服务器上如/data/ofm/benchmark/并把脚本内path_list_txt指向你的路径文件。脚本默认的url_base /planet/fake_version/中的fake_version是占位符——在实际压测中nginx 的 wildcard location 会匹配任意版本号见 nginx.py 中生成的location ~ ^/{area}/([^/])/...规则因此无需修改即可命中瓦片路由。重启 nginx 清缓存并压测service nginx restart wrk -c10 -t4 -d60s -s /data/ofm/benchmark/wrk_custom_list.lua http://localhost如果只想快速验证可将-d60s缩短为-d10s。验证结果检查 wrk 输出中的Requests/sec、Transfer/sec与Socket errors。对照本文的两组数据如果你的服务器使用本地 SSD冷缓存下应能观察到远高于网络带宽上限的本地吞吐若结果异常偏低或出现大量 timeout则需要排查存储介质与 nginx 配置。结语用数据验证架构选择OpenFreeMap 的这份基准测试记录是一份相当典型的“用真实负载验证架构假设”的工程文档它证明了“Btrfs 镜像 硬链接 nginx 静态服务”这一反直觉的瓦片托管方案在合适的硬件上完全具备生产级性能47k req/s、3.84GB/s、零错误也通过 BuyVM 的对照组清晰展示了远程存储在该负载下的局限。对于任何打算自托管 OpenFreeMap 的开发者results.md 与 README.md 提供了可直接复用的压测方法论而 wrk_custom_list.lua 与 nginx_to_path_list.py 则是开箱即用的工具集——正如 README.md 所说欢迎提交你自己的压测结果附主机环境让这份基准数据库不断丰富。【免费下载链接】openfreemapFree and open-source map hosting solution with custom styles for websites and apps, using OpenStreetMap data项目地址: https://gitcode.com/GitHub_Trending/op/openfreemap创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价