Hono HTTP Benchmark 使用指南:用 bombardier 对比 main 与当前分支的 HTTP 性能
【免费下载链接】honoWeb framework built on Web Standards项目地址: https://gitcode.com/GitHub_Trending/ho/hono
本指南基于 Hono 仓库中的 benchmarks/http-server/README.md 及其配套实现 benchmarks/http-server/benchmark.ts,系统讲解 Hono 官方 HTTP 性能基准测试工具的设计思路、运行方式与结果解读。读完本文,你将掌握如何在本机一键对比main分支与当前工作区的 HTTP 吞吐差异,理解测试场景、命令行参数与底层执行流程,并能在提交 Pull Request 前自行复现同样的性能评估。
工具定位:它测的是什么
Hono HTTP Benchmark 是一套面向 HTTP 层面的性能基准测试脚本,核心目标只有一个:对比main分支与当前代码两个版本的 Hono 在真实 HTTP 请求下的吞吐表现。它属于"端到端"式压测——通过 bombardier 这类负载工具向真实启动的服务器发送并发请求,衡量整体处理能力。
它与仓库中的另一套基准测试 benchmarks/fetch/README.md(fetch benchmark)形成互补:
- fetch benchmark:在进程内直接调用
app.fetch(),衡量的是框架路由与响应构造本身的 CPU 开销(纳秒级); - HTTP benchmark:通过真实的 TCP 连接发起并发 HTTP 请求,衡量的是包含运行时(Bun)、HTTP 解析、响应序列化在内的端到端吞吐(Req/s)。
因此,HTTP benchmark 更适合回答"实际部署时整体吞吐有没有回退"这类问题,而 fetch benchmark 更适合定位框架内部的微秒级开销变化。
快速开始
前置条件
按 benchmarks/http-server/README.md 的说明,运行该基准测试需要满足两个前提:
| 依赖 | 要求 | 说明 |
|---|---|---|
| Bun | v1.0+ | 基准脚本本身用 Bun 运行,且被测服务器也由 Bun 启动(bun <app.ts>) |
| bombardier | 任意版本 | 负载生成工具;macOS 可用brew install bombardier安装,其他平台见其官方安装说明 |
从脚本实现看,Bun 之所以是必选项,是因为 benchmark.ts 使用node:child_process的spawn拉起子进程,测试应用模板通过import { Hono } from './src/index.ts'直接引用 TypeScript 源码(见 benchmark.ts),这依赖 Bun 原生的 TS 执行能力。
运行命令
在仓库根目录执行:
cd benchmarks/http-server bun run benchmark.ts脚本运行后会在终端输出汇总表格,同时将结果写入benchmarks/http-server/benchmark-results.md。整个流程大致是:准备基线版本 → 准备目标版本 → 依次压测 → 计算变化率 → 输出 Markdown 表格。
在 Pull Request 中的自动运行
README 明确说明:每个 Pull Request 都会自动运行 HTTP 基准测试,并把结果以评论形式回复到 PR 上。这意味着该工具是 Hono 项目 CI 的一部分,用于防止性能回归合入主干。对本仓库的贡献者而言,本地先跑一遍基准测试,是提交前验证性能无回退的推荐做法。
命令行选项详解
脚本在 benchmark.ts 的头部注释中列出了全部可用选项,实现处见 benchmark.ts:
bun run benchmark.ts [options] 选项: --baseline=<ref> Git 引用,作为性能基线(默认: origin/main) --target=<ref> Git 引用,作为被测目标(默认: current,即当前工作区) --runs=<number> 每个基准的重复轮数(默认: 1) --duration=<number> 每轮压测时长,单位秒(默认: 10) --skip-tests 跳过端点正确性校验测试| 选项 | 默认值 | 作用 |
|---|---|---|
--baseline=<ref> | origin/main | 基线版本的 Git 引用(分支、tag 或 commit hash),会被检出到临时环境与目标版本对比 |
--target=<ref> | current | 被测版本;默认值current直接使用当前工作区源码,无需任何 Git 操作 |
--runs=<number> | 1 | 重复压测轮数,结果取多轮平均值,轮数越多越稳定 |
--duration=<number> | 10 | 每条 bombardier 压测命令的持续时间(秒),时间越长样本越充分 |
--skip-tests | 关闭 | 跳过压测前的端点校验,适合只需粗略吞吐数据的场景 |
两个被写死、不可通过命令行调整的关键常量也值得注意(见 benchmark.ts):
- 并发数固定为
500(const concurrency = 500); - 三个压测场景(ping / query / body)分别对应三条固定的 bombardier 命令。
实际使用示例
# 默认对比 origin/main 与当前工作区,每轮压测 10 秒 bun run benchmark.ts # 对比某个 tag 与当前工作区,压测 3 轮、每轮 20 秒 bun run benchmark.ts --baseline=v4.12.0 --runs=3 --duration=20 # 跳过端点校验,快速跑一遍 bun run benchmark.ts --skip-tests被测应用:三个典型场景
无论 baseline 还是 target,被测的都是同一个应用模板(由getAppTemplate()生成,见 benchmark.ts),它覆盖了 Hono 最常见的三种路由形态:
import { Hono } from './src/index.ts' import { RegExpRouter } from './src/router/reg-exp-router/index.ts' const app = new Hono({ router: new RegExpRouter() }) app .get('/', (c) => c.text('Hi')) .post('/json', (c) => c.req.json().then(c.json)) .get('/id/:id', (c) => { const id = c.req.param('id') const name = c.req.query('name') c.header('x-powered-by', 'benchmark') return c.text(`${id} ${name}`) }) export default app对应脚本中的三个压测场景(见 benchmark.ts):
| 场景 | 路由 | 压测命令要点 | 考察点 |
|---|---|---|---|
| ping | GET / | bombardier --fasthttp -c 500 -d 10s http://127.0.0.1:3000/ | 静态路由 + 纯文本响应的最短路径吞吐 |
| query | GET /id/1?name=bun | bombardier --fasthttp -c 500 -d 10s http://127.0.0.1:3000/id/1?name=bun | 带路径参数:id与查询参数name的动态路由吞吐 |
| body | POST /json | bombardier --fasthttp -c 500 -d 10s -m POST -H Content-Type:application/json -f body.json http://127.0.0.1:3000/json | 请求体解析(c.req.json())与 JSON 响应(c.json())吞吐 |
值得注意的两个设计细节:
- 路由策略被固定为 RegExpRouter:模板通过
new Hono({ router: new RegExpRouter() })显式指定正则路由。Hono 的 RegExpRouter 基于 Trie 构建后合并为单个正则进行匹配(实现见 src/router/reg-exp-router/router.ts 的通配正则缓存构建逻辑),是 Hono 面向运行时吞吐优化的默认选择之一; - request body 文件:
setupTemp()会在临时目录生成body.json(内容为{"hello":"world"},见 benchmark.ts),供 POST 场景通过-f参数作为请求体发送。
执行流程:从 Git 检出到结果汇总
benchmark.ts 的main()函数串联了完整流水线,下面按阶段拆解。
阶段一:准备 baseline 与 target
buildVersion()(见 benchmark.ts)负责把两个版本各自的src目录与测试应用复制到隔离的临时目录:
- target 为
current时直接使用当前工作区,不做任何 Git 操作; - baseline 为非
current引用时,依次执行:git fetch origin拉取最新远程引用;git stash push暂存当前未提交改动(若存在,并记录stash@{0}以便恢复);git checkout <version>检出目标版本;bun install --frozen-lockfile安装该版本的锁定依赖;- 拷贝
src目录与生成的应用模板到临时目录; - 最后
git checkout -切回原分支,并按需git stash pop恢复暂存内容。
临时目录位于benchmarks/http-server/.benchmark-temp(TEMP_DIR),结束时会整体清理。
阶段二:端点正确性校验
若未指定--skip-tests,脚本会先以NODE_ENV=production启动被测服务器(见 benchmark.ts),用fetch逐一对三个端点做断言:
GET /返回体必须为Hi;GET /id/1?name=bun必须返回1 bun且响应头x-powered-by为benchmark;POST /json返回体必须与请求体一致,且content-type包含application/json。
校验通过会打印✅ Tests passed for <name>,任何断言失败都会抛出异常终止基准测试。这一步保证了"压测的是功能正确的代码",避免把明显损坏的版本纳入对比。
阶段三:bombardier 压测与结果解析
runBenchmark()(见 benchmark.ts)为每个版本启动服务器后,按 ping → query → body 顺序执行三条 bombardier 命令,每条命令都使用--fasthttp(启用 fasthttp 压测引擎)与固定 500 并发。每轮结束后用正则/Reqs\/sec\s+(\d+[.|,]\d+)/从输出中解析Reqs/sec数值(解析失败记为 0),最终得到三个场景各自的平均吞吐与总体平均值:
ping:三个场景的平均Reqs/sec分别记为 ping、query、body;overall:三者算术平均,作为该版本的综合得分。
多轮运行时(--runs> 1),每轮都会重启服务器(spawn('bun', [appPath], ...),NODE_ENV=production),再对每轮结果求平均,以降低冷启动、GC 抖动等偶然因素影响。
阶段四:变化率计算与结果输出
calculateChange()用((target - baseline) / baseline) * 100计算每个指标的百分比变化(见 benchmark.ts),正数表示 target 更快、负数表示回退。输出包含两部分:
- 终端:打印
Framework / Runtime / Average / Ping / Query / Body三行表格(baseline、target、Change),数字格式化为千分位,变化率带+/-前缀; - 文件:同一表格以
## HTTP Performance Benchmark为标题写入benchmarks/http-server/benchmark-results.md,供 CI 或 PR 评论引用。
结果解读示例
一次典型运行结束后,终端会输出类似如下的结构(数值为格式示意,具体结果取决于机器与版本):
| Framework | Runtime | Average | Ping | Query | Body | | --- | --- | --- | --- | --- | --- | | hono (origin/main) | bun | 123,456.78 | 130,000.00 | 120,000.00 | 120,370.34 | | hono (current) | bun | 125,000.00 | 132,000.00 | 121,500.00 | 121,500.00 | | Change | | +1.25% | +1.54% | +1.25% | +0.94% |判断口径:Change行为正说明当前分支相对基线更快,为负则说明存在回退风险,需要结合具体改动定位原因。注意本工具对比的是同一个框架的两个 Git 版本,并不产出与第三方框架的横向排名,因此不宜将这里的数字与其他仓库的 benchmark 直接比较。
延伸到其他基准测试:fetch 与 routers
HTTP benchmark 不是仓库中唯一的性能工具,理解它在你需要更细粒度定位时可与其他两套配套使用:
- benchmarks/fetch:进程内测量
app.fetch()开销。./compare.sh会在每轮用独立进程分别测工作区与某个 Git 引用(默认main),并每轮反转两者顺序,避免同进程内 JIT/GC 预热与执行顺序造成偏差;支持RUNTIME=node、ROUNDS=5等环境变量,最终用 benchmarks/fetch/summarize.mts 汇总每轮平均值的中位数与全部原始轮次值。当 HTTP 层出现回退而想确认是不是路由/响应构造本身的瓶颈时,可以用它快速定位; - benchmarks/routers:面向路由器本身的横向对比,覆盖 find-my-way、express、koa-router、Hono RegExpRouter、Hono TrieRouter 等常用路由实现,
bun run bench:bun与bun run bench:node分别测 Bun 与 Node 环境。若怀疑吞吐差异源于路由选择,可参考其结果。
注意事项与适用前提
- 被测运行时是 Bun:应用由
bun启动(见 benchmark.ts),因此结果反映的是 Bun 运行时下的 Hono 表现,不代表 Node.js、Deno、Cloudflare Workers 等环境; - 固定并发 500:并发数不可调,对高延迟场景或资源受限机器可能造成过度饱和,解读数字时需结合本机硬件;
- 需要 Git 引用可访问:默认 baseline 为
origin/main,首次运行会执行git fetch origin,离线环境下需改用本地分支/tag(如--baseline=v4.12.0); - 会临时改动工作区:baseline 构建过程中涉及
git stash/git checkout,虽然结束后会恢复,仍建议在干净的提交状态下运行,避免意外;临时目录.benchmark-temp在结束时自动删除。
小结
Hono HTTP Benchmark 是一个轻量而完整的性能回归守护工具:一条bun run benchmark.ts命令即可完成"检出基线 → 校验端点 → bombardier 压测 → 计算变化率 → 输出 Markdown 报告"的全流程,其 PR 自动评论机制让性能变化在合入前即可见。结合 benchmarks/fetch 的进程内开销测量与 benchmarks/routers 的路由器横向对比,你可以从"整体吞吐 → 框架开销 → 路由层"三个粒度全面评估 Hono 的性能特征。
【免费下载链接】honoWeb framework built on Web Standards项目地址: https://gitcode.com/GitHub_Trending/ho/hono
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考