http/grpc入口需手动注入x-test-flag,因go无运行时aop,中间件context不自动透传;dao/redis/es/kafka等须基于ctx动态路由并加stress前缀;第三方依赖应interface+mock注入;压测须走真实网络栈并隔离环境。

HTTP 和 gRPC 入口必须手动注入 X-Test-Flag,不能依赖中间件自动透传
Go 没有运行时 AOP 机制,中间件的 context.Context 不会自动进入 goroutine 或下游 RPC 调用。如果只在通用 HTTP middleware 里做 context.WithValue(r.Context(), testKey, true),后续 handler 启的 go func() { db.Query(...) } 就拿不到标识,DB 层照样写主库。
实操建议:
- HTTP handler 第一行就检查:
if flag := r.Header.Get("X-Test-Flag"); flag == "1" { ctx = context.WithValue(r.Context(), testKey, true) },testKey必须是私有 struct 类型(如type testKey struct{}),避免 key 冲突 - gRPC server interceptor 中,用
metadata.FromIncomingContext(ctx)提取X-Test-Flag,再塞进新ctx;client interceptor 发请求前,用metadata.AppendToOutgoingContext(ctx, "X-Test-Flag", "1")回写 - Nginx 默认
large_client_header_buffers 4 4k,压测头多(X-Trace-ID+X-Scene-ID+ 自定义字段)容易截断,需调大
DAO 和 Redis 必须基于 context 动态路由,禁止 SQL 或 Key 硬编码前缀
在 GORM 的 db.Create(&order) 前硬拼 "INSERT INTO stress_orders",GORM 的预编译、事务、Session 缓存全失效,语句生成仍按原表名,最终报错或误写主库。
实操建议:
- DAO 初始化时传入两个
*sql.DB:一个prodDB,一个stressDB;每次执行Query/Exec前查ctx.Value(testKey),动态选连接池 - Redis 封装
redis.Client,Get(key)方法内检查ctx.Value(testKey),压测时自动把"user:1001"映射为"stress_user_1001" - Elasticsearch 索引名、Kafka topic、consumer group id 都要加
stress_前缀,且完全隔离——不能复用线上 group id
第三方依赖必须用 interface + mock 注入,不能靠 http.RoundTripper 拦截兜底
支付、短信等外部服务在压测中既不现实也不合规。仅靠 http.RoundTripper 拦截 sms-api.example.com 返回 mock JSON,看似简单,但一旦服务内部用了非标准 HTTP client(比如自定义 TLS config、重试逻辑),拦截器就漏掉真实调用。
实操建议:
- 把第三方 client 定义为 interface(如
type SmsClient interface { Send(...)}),启动时根据环境变量ENV=stress注入 mock 实现 - mock 实现里直接返回预设结构体,不走网络;真实 client 只在非 stress 环境注入
- 若已有代码无法改 interface,再退一步用
http.RoundTripper,但必须覆盖所有可能路径:包括http.DefaultClient、自定义http.Client、kitex/gRPC 的 HTTP fallback 路径
压测流量必须走真实网络栈,vegeta/wrk/ghz 要禁用 KeepAlive
go test -bench 绕过 TCP 栈、TLS、连接池、accept 队列,测出来的是 handler 函数在内存里的速度,和线上扛不扛得住毫无关系。它暴露不了 TIME_WAIT 泛滥、连接池打满、fd 耗尽这些真瓶颈。
实操建议:
- HTTP 压测用
vegeta或wrk,必须加-H "Connection: close"(wrk)或确保DisableKeepAlives: true(vegeta 默认关,wrk 默认开) - gRPC 压测统一用
ghz,命令里显式带--keepalive=false和--metadata 'X-Test-Flag:1' - 压测机与服务端必须物理或网络隔离,避免 CPU、网卡争抢;每次压测前清 page cache、重启服务、手动触发两次
runtime.GC() - 边压边采
/debug/pprof/profile?seconds=30、/heap、/goroutine?debug=2,三次独立运行取 QPS 和 P99 中位数
context.Context 的“无感使用”——日志、metrics、trace SDK 若没封装成 log.WithContext(ctx) 这类接口,压测标识一进去就丢,排查时根本分不清哪条日志来自压测流量。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











