goreplay是Go语言实现的HTTP流量录制回放工具,通过监听网卡端口(如--input-raw :8080)捕获真实请求,保留方法、Header、Body、时间戳等完整上下文,支持文件存储(.gor)、倍速回放、流量过滤与脱敏,无需改代码或代理,适用于Nginx/API网关等入口层压测。

Go语言实现HTTP请求重放,不能靠 go test -bench 或手写循环发请求糊弄过去——它压不出 header 透传、JWT 签名、超时级联、连接池耗尽这些线上真实问题。必须从入口层捕获真实流量,再可控回放。
用 goreplay 录制生产 HTTP 流量最直接
goreplay 不需要改代码、不依赖代理配置,直接监听网卡或端口,适合在 Nginx 入口、API 网关或服务宿主机上部署。它把原始 TCP 层的 HTTP 流量按协议解析后存为 .gor 文件,保留了方法、路径、Header、Body、时间戳等全部上下文。
-
--input-raw :8080是最常用启动方式,监听本地 8080 端口所有进出流量 - 录制时务必加
--output-file requests.gor,否则只打印到 stdout,无法回放 - 别用
--input-raw 127.0.0.1:8080—— 这只会抓本机主动发起的请求,漏掉外部进来的流量;应写成--input-raw :8080(冒号开头表示监听所有接口) - 如果服务启用了 HTTPS,goreplay 默认不解密 TLS,录下来的只是加密流;想录明文需配合 mitmproxy 做中间人,或改用 clawgate 这类支持 TLS 终止的代理型工具
回放时要注意请求体和 Header 的“时效性”
直接回放 .gor 文件,常遇到 401、403、429 或签名验证失败——不是工具不行,是原始请求里带了过期的 Authorization、X-Timestamp、X-Nonce 或动态 token。
- 用
--http-allow-header Authorization可以保留认证头,但更稳妥的做法是回放前过滤或重写:--http-modify-header "Authorization:"清空它,再用--http-set-header "Authorization: Bearer ${TOKEN}"注入新值 -
--input-file-loop配合--rate 500%能实现倍速循环压测,但注意:原始请求间的时间间隔会被同比放大,若原流量有 10ms 间隔,500% 就变成 2ms,可能瞬间打垮下游 - goreplay 默认不重放响应,只发请求;如果你需要校验响应内容做断言,得自己接
--output-http后用脚本抓返回,或换用 GoPaw 这类支持 HAR 导出+响应比对的工具
敏感字段不剥离,回放就是生产事故
录下来的 .gor 文件里,很可能包含 Authorization: Bearer xxx、Cookie: session=...、手机号、身份证号等敏感信息。直接回放到测试环境,轻则触发风控封禁,重则泄露数据。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
立即学习“go语言免费学习笔记(深入)”;
- 录制阶段就该过滤:
--http-disallow-header "Authorization,Cookie,Set-Cookie" - 或用
--http-modify-body 's/{"phone":"\d+"}/{"phone":"138****1234"}/g'正则脱敏(注意 goreplay 的 body 修改只支持简单正则,复杂结构建议先转成 HAR 再用 GoPaw 处理) - 别信“测试环境没敏感数据”——只要请求里带了生产 JWT,下游服务一验签就可能调用真实支付、发短信、查征信
- clawgate 和 GoPaw 支持规则引擎写 Lua/Go 脚本,在录制/回放时动态抹除或替换字段;goreplay 没这能力,硬要这么做就得写 wrapper 脚本预处理
.gor文件
Go 原生代码里嵌入录制逻辑要防 Body 读取陷阱
如果不想依赖外部工具,想在服务里加个中间件自动录请求,最容易踩的坑是 Request.Body 只能读一次。你一读它就 EOF,后续 handler 拿不到 body,整个请求就废了。
- 必须用
io.TeeReader(r.Body, buf)把 body 同时写进bytes.Buffer存档,再用r.Body = io.NopCloser(buf)重置回去 - 如果上游用了 gzip,
Content-Encoding: gzip的 body 会是压缩流,直接存下来回放时服务端可能不识别;得先gzip.NewReader(r.Body)解压,再 tee 到 buffer - 时间戳别用
time.Now(),而应从X-Forwarded-For、X-Real-IP或原始日志里提取,否则回放时间全是录制时刻,链路追踪全乱 - 用
http.ServeMux+ 中间件录制时,记得在next.ServeHTTP前完成 body 读取和重置;顺序错了,body 就没了
真正难的从来不是“怎么放出去”,而是“怎么放得像真的”——header 是否透传、签名是否有效、超时是否级联、熔断是否触发。这些细节决定了录制回放到底是压测还是自欺欺人。

















