☰
Hey 源码结构全景图:HTTP 压测工具 hey.go 与 requester 包的完整架构导览
2026/10/3 12:35:01 网站建设 项目流程

Hey 源码结构全景图:HTTP 压测工具 hey.go 与 requester 包的完整架构导览

【免费下载链接】heyHTTP load generator, ApacheBench (ab) replacement项目地址: https://gitcode.com/GitHub_Trending/he/hey

hey 是一款用 Go 语言编写的轻量级 HTTP 负载生成器(load generator),常被视为 ApacheBench(ab)的现代替代品。整个项目只有两个入口文件加一个 requester/ 包,代码量极小,却是学习"并发压测工具如何设计"的绝佳样本。本文带你从 hey.go 走到 requester/requester.go,看清每一个模块的分工。

一、仓库文件地图:先看全景再拆细节

整个仓库非常扁平,核心就 8 个文件:

文件职责
hey.go程序入口:命令行参数解析、请求构造、启动压测
hey_test.go入口层的单元测试
requester/requester.go压测引擎核心:Work结构体与 worker 并发调度
requester/report.go结果聚合:延迟统计、百分位、直方图
requester/print.go输出渲染:Summary / CSV 两种模板
requester/now_other.go非 Windows 平台的高精度计时
requester/now_windows.goWindows 平台计时(QueryPerformanceCounter)
requester/requester_test.gorequester 包的测试

周边工程文件还有 go.mod(仅依赖golang.org/x/net一个模块)、Makefile(跨平台构建 release 产物)和 Dockerfile。这种"入口 + 单包"的结构,让 hey 可以编译成零依赖的单个二进制文件。

二、入口层:hey.go 如何把命令行变成压测任务

hey.go 约 290 行,承担了全部"胶水"工作,逻辑可以拆成四步 🧩:

1. 命令行参数定义

文件开头用 Go 标准库flag声明了所有参数(hey.go#L41-L67):

  • -n请求总数(默认 200)、-c并发 worker 数(默认 50)
  • -q每 worker 的 QPS 限流、-z压测持续时长
  • -m/-H/-d/-D控制方法、请求头、请求体
  • -h2开启 HTTP/2,-cpus控制 CPU 核数

2. 参数校验与请求构造

main()函数(hey.go#L106-L253)先做合法性校验(如-n不能小于-c),随后把参数翻译成标准库的http.Request:正则解析自定义头、Basic Auth,拼装 User-Agent(自动带上hey/0.0.1标识)。

3. 装配 Work 并启动

关键在 hey.go#L224-L252:把所有配置塞进requester.Work结构体,调用w.Init()和w.Run()。同时它注册了两个"停止信号"——

  • Ctrl+C:通过os/signal捕获中断信号,触发w.Stop()
  • -z定时停止:后台 goroutine 睡满指定时长后自动调用w.Stop()

也就是说,hey.go 自己不执行任何 HTTP 请求,它只负责"配置 + 启停控制",真正的引擎在 requester 包里。

三、引擎层:requester 包的并发模型

requester 包是 hey 的心脏,四个文件各司其职。

Work 结构体:一次压测的全部状态

requester/requester.go 中的Work(requester.go#L49-L100)持有压测配置(N/C/QPS/Timeout/H2 等),以及两个关键通道:

  • results chan *result:结果管道,缓冲上限 100 万条
  • stopCh chan struct{}:优雅停机信号

生命周期只有四个方法:Init()→Run()→(外部可触发)Stop()→Finish()。

runWorkers:C 个并发 worker 怎么跑

requester.go#L235-L264 的runWorkers()展示了 hey 最核心的并发设计:

  1. 构建一个共享的http.Transport——MaxIdleConnsPerHost取并发数 C(上限 500),让 TCP 连接在 worker 间复用;
  2. 若开启-h2,用http2.ConfigureTransport升级传输层;
  3. 启动 C 个 goroutine,每个执行runWorker(client, N/C),用sync.WaitGroup等待全部完成。

每个 worker 内部(requester.go#L208-L233)用time.Ticker实现 QPS 限流,并在每轮循环开头检查stopCh,保证收到停止信号后能立刻退出。

makeRequest:一次请求的"分段计时"

requester.go#L146-L206 是理解 hey 统计精度的关键。它通过httptrace.ClientTrace钩住请求生命周期的 6 个时间点,把一次请求拆成独立耗时:

阶段含义
DNS+dialup建立 TCP 连接总耗时
DNS纯 DNS 解析耗时
req write写出请求耗时
resp wait首字节延迟(TTFB)
resp read读取完整响应耗时

最终打包成一个result(requester.go#L36-L47)投递到results通道。这套"生产者(worker)→ 管道(channel)→ 消费者(reporter)"模型,让压测与统计互不阻塞。

四、统计层:从原始结果到 Summary 报告

report.go:聚合器

requester/report.go 中的runReporter(report.go#L84-L114)在独立 goroutine 里循环消费results通道,累计各阶段耗时之和、状态码分布、错误分布。

snapshot()(report.go#L143-L215)在压测结束时对延迟切片排序,产出:

  • 响应时间直方图:等宽分成 10 个桶(histogram(),report.go#L237-L268)
  • 延迟分布:P10 / P25 / P50 / P75 / P90 / P95 / P99 七个百分位(latencies(),report.go#L217-L235)

注意maxRes = 1000000(report.go#L31):明细数据最多保留 100 万条,超大压测下靠均值/直方图兜底,防止内存爆炸。

print.go:两种输出模板

requester/print.go 用 Go 的text/template定义了两套渲染模板(print.go#L96-L127):

  • 默认 Summary:总量、最快/最慢/平均耗时、RPS、直方图、百分位、各阶段明细、状态码与错误分布
  • CSV(-o csv):每个请求一行,8 列指标,方便导入表格工具二次分析

文件头部的注释(print.go#L15-L34)对两种格式的字段解释得非常清楚,读代码时可直接当作文档用。

now_*.go:跨平台高精度计时

压测工具对计时精度极其敏感,因此now()做了平台拆分:

  • now_other.go:其他平台直接用time.Since,以包级startTime为零点
  • now_windows.go:Windows 下调用QueryPerformanceCounter系统调用,绕开time.Now()的微秒级精度瓶颈

这是 Go 构建标签(build tag)做平台差异化的典型用法。

五、一次压测的完整数据流

把各模块串起来,hey 的执行链路是:

  1. hey.go 解析参数 → 构造http.Request→ 填充requester.Work
  2. Work.Run()启动 reporter goroutine + C 个 worker goroutine(requester.go#L119-L129)
  3. 每个 worker 循环调用makeRequest,带分段计时结果写入results通道
  4. reporter 实时消费、累计;worker 全部Done后Finish()关闭通道
  5. reporter 排序、分桶、算百分位,finalize()触发模板渲染,输出 Summary
  6. 期间 Ctrl+C 或-z到期 →Stop()向stopCh发 C 个信号 → workers 优雅退出

六、这套架构值得新手借鉴的 4 个设计点

  • 入口与引擎解耦:hey.go 不含任何 HTTP 逻辑,requester 包可以脱离命令行独立使用(这也是 requester_test.go 能直接测引擎的原因)
  • channel 解耦生产/消费:worker 只管发请求,reporter 只管统计,二者互不等待,压测吞吐不受统计开销影响
  • 优雅停机:stopCh信号 + 每轮循环检查,避免"发了 99% 的请求还停不下来"
  • 模板化输出:统计逻辑(report)与展示逻辑(print 模板)分离,加一种输出格式只需加一个模板串

七、如何上手阅读这份代码 🚀

建议的阅读顺序与 README.md 中的示例配合:

  1. 先在终端跑一遍hey -n 100 -c 10 https://example.com,直观感受输出格式
  2. 从 hey.go 的main()顺藤摸瓜到Work.Run()
  3. 精读 requester/requester.go 的makeRequest与runWorkers
  4. 再看 report.go 的snapshot(),理解直方图与百分位是怎么算出来的

如果想从源码构建,clone 仓库后执行make release即可生成三大平台的二进制(见 Makefile);日常开发只需go build。

hey 用不到 1500 行核心代码实现了生产级压测工具的完整能力——入口解析、并发引擎、分段计时、百分位统计、优雅停机——是学习 Go 并发与压测原理不可多得的精简范本。

【免费下载链接】heyHTTP load generator, ApacheBench (ab) replacement项目地址: https://gitcode.com/GitHub_Trending/he/hey

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询