正确做法是起一个中间代理,用httputil.newsinglehostreverseproxy监听localhost:8081,在roundtrip中插入time.sleep模拟延迟或io.limitreader限速,客户端通过proxy设置指向该代理,服务端保持原样,避免污染业务逻辑且真实复现网络行为。

用本地延迟代理模拟低带宽,别改业务代码
直接在 HTTP 客户端里加 sleep 或限速逻辑,会污染业务逻辑、难以复用、且无法模拟真实 TCP 窗口与缓冲行为。正确做法是起一个中间代理,把测试客户端指向它,后端服务保持原样。
实操建议:
- 用
httputil.NewSingleHostReverseProxy起一个反向代理,监听localhost:8081 - 在
RoundTrip方法里插入time.Sleep(200 * time.Millisecond)模拟固定延迟,或用io.LimitReader限制响应体读取速率(如 100KB/s) - 测试时把 client 的
http.DefaultClient.Transport的Proxy设为http.ProxyURL(&url.URL{Scheme: "http", Host: "localhost:8081"}) - 避免用
net/http/httptest—— 它走 loopback,内核会绕过真实网络栈,测不出带宽瓶颈
客户端必须用 io.CopyN 精确测传输阶段耗时
http.Get 返回的 resp.Body 包含 DNS、建连、TLS 握手等非传输时间,而带宽只关心“数据在链路上跑的速度”。计时起点必须是收到响应头之后、开始读 body 的瞬间。
常见错误写法:start := time.Now(); resp, _ := http.Get(url); io.CopyN(...); duration := time.Since(start) —— 这把建连时间全算进去了。
正确做法:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 调用
resp, err := client.Do(req)后,立刻执行defer resp.Body.Close() - 紧接着用
start := time.Now(); io.CopyN(ioutil.Discard, resp.Body, 1024*1024)(比如测 1MB 传输) - 再用
duration := time.Since(start)计算纯传输耗时 - 务必禁用服务端压缩:
w.Header().Set("Content-Encoding", "identity"),否则 gzip 会扭曲字节数
服务端响应体要可控,别用 fmt.Sprintf 或 strings.Repeat
测带宽的核心是“单位时间稳定发出多少字节”,响应体必须长度精确、无额外内存抖动。字符串拼接类操作会触发 GC、分配不可控内存,干扰吞吐测量。
推荐做法:
- 用
bytes.Repeat([]byte("0"), size)生成固定长度响应体,例如http.ServeFile不适用,它可能加 header、做 range 请求处理 - 手动设置
w.Header().Set("Cache-Control", "no-cache"),防止中间代理缓存 - 返回前调用
w.Header().Set("Content-Length", strconv.Itoa(size)),避免 Transfer-Encoding: chunked 引入编码开销 - 不要用
resp.ContentLength判断——有些服务端不设该 header,或设为 -1,应以你主动写的字节数为准
验证是否真卡在带宽,先排除 GOPROXY 和 DNS 干扰
如果你在 go run main.go 时发现请求慢,第一反应不该是调低带宽,而是确认这慢是不是来自模块下载或解析阶段。Go 工具链在首次运行时会隐式触发 go mod download,若 GOPROXY 没生效,就会卡在 proxy.golang.org 的 TLS 握手超时上。
快速排查步骤:
- 运行
go env GOPROXY,输出必须是https://goproxy.cn,direct(注意逗号后direct不能少) - 执行
go mod download -x github.com/gorilla/mux@v1.9.0,看日志里是GET https://goproxy.cn/...还是GET https://proxy.golang.org/... - 如果看到后者,说明子进程没继承环境变量,需在 Shell 中执行
export GOPROXY="https://goproxy.cn,direct"再试 - 临时加
go env -w GODEBUG=netdns=cgo可绕过 Go 自带 DNS 解析器,避免因 DNS 慢导致的假性“低带宽”现象
真正影响带宽表现的,永远是传输层行为本身;但多数人第一次测不准,是因为还没让 Go 环境安静下来——模块拉取、DNS 解析、校验服务器这些前置环节,比 TCP 发包慢得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










