混沌测试需精准注入可观测、可配置、可回滚的扰动,并与go http生命周期对齐:客户端混沌用自定义roundtripper,服务端混沌用中间件,底层问题则通过http.server超时配置和系统工具触发。

混沌测试不是加个 panic 就完事
直接在 API handler 里插 panic("chaos") 或随机 http.Error,只会让测试不可控、难复现、上线前漏掉真实故障路径。真正的混沌测试要能精准注入延迟、错误、超时、网络分区等可观测、可配置、可回滚的扰动,且必须和 Go 的 HTTP 生命周期对齐——比如在中间件层拦截请求,而不是侵入业务逻辑。
用 net/http.RoundTripper 拦截客户端调用做混沌
如果你的 API 服务会主动调用下游(如调第三方 HTTP 接口),这是最干净的混沌注入点:不改 handler,不影响路由,所有出向请求自动受控。核心是自定义 RoundTripper,在 RoundTrip 方法里判断目标 host/path,按策略注入故障:
- 延迟:用
time.Sleep模拟慢响应,注意别阻塞整个 goroutine(建议用context.WithTimeout包裹原请求) - 错误:直接返回
&url.Error{Err: errors.New("connection refused")},比返回 5xx 更贴近底层失败 - 超时:把原 request.Context() 替换为带更短 deadline 的新 context
- 注意:
RoundTripper是无状态的,所有策略参数(如故障率、延迟范围)需通过闭包或结构体字段传入,避免全局变量
用中间件在 http.Handler 层注入服务端混沌
针对本服务接收的请求做混沌(如模拟 DB 超时、缓存雪崩),推荐写一个标准中间件,包裹 http.Handler:
func ChaosMiddleware(next http.Handler, cfg ChaosConfig) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if shouldInject(cfg, r) {
switch cfg.Type {
case "delay":
time.Sleep(cfg.Delay)
case "error":
http.Error(w, "chaos injected", cfg.StatusCode)
return
case "timeout":
ctx, cancel := context.WithTimeout(r.Context(), cfg.Timeout)
defer cancel()
r = r.WithContext(ctx)
}
}
next.ServeHTTP(w, r)
})
}
关键点:
-
shouldInject必须支持按 path、header(如X-Chaos-Enabled)、甚至 query 参数开关混沌,否则压测时全量生效会拖垮环境 - 注入
error后必须return,否则后续next.ServeHTTP还会执行,造成重复响应 - 修改
r.Context()后,下游 handler 必须真正使用该 context(例如传给db.QueryContext),否则 timeout 不生效
别忽略 http.Server 的 ReadTimeout 和 WriteTimeout
很多混沌场景其实根本不用写代码——直接调大 http.Server.ReadTimeout,再用工具(如 tc)在宿主机层制造网络延迟,就能复现“请求卡在连接建立阶段”的问题;设小 WriteTimeout 则能触发“流式响应中途断开”。这些配置会影响所有请求,但好处是:它们和 Go runtime 的 netpoll 机制深度绑定,能暴露 goroutine 泄露、连接池耗尽等底层问题,而中间件做不到这点。
容易被忽略的是:Go 1.22+ 默认启用了 http.Server.IdleTimeout,如果混沌中故意不发完整请求(如只发 header 就停住),这个 timeout 会提前关闭连接,掩盖你真正想测的长连接挂起行为——测试前记得显式设为 0 或足够大。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











