4. 压测环境搭建与数据采集
本章聚焦于搭建一套可复现的压测环境,并配置端到端的延迟监控链路。目标是让服务端和客户端的延迟数据能够被精确采集、对齐和可视化,为后续的尾延迟诊断提供可靠的数据基础。
4.1 压测环境拓扑
采用三节点集群部署:1 台压测客户端 + 2 台 Go 服务节点(负载均衡)。所有节点位于同一内网,避免网络抖动干扰。
环境规格:CPU 4 核 / 内存 8 GB / 磁盘 SSD / 内核参数 net.core.somaxconn=65535 / 关闭 swap。
4.2 压测工具选型与配置
| 工具 | 适用场景 | 延迟精度 | 配置要点 |
|---|---|---|---|
| wrk2 | 固定 QPS 压测,尾延迟敏感 | 微秒级 | --latency --rate 1000 -t 4 -c 40 -d 120s |
| ghz | gRPC 压测,协议级延迟 | 毫秒级 | --rps=1000 --concurrency=10 --duration=120s |
| hey | 快速验证,HTTP 压测 | 毫秒级 | -q 1000 -c 10 -z 120s |
推荐:尾延迟诊断优先使用 wrk2,因为它能精确控制发送速率并输出完整的延迟分布(P50/P90/P99/P99.9)。
4.3 服务端延迟采集
在 Go 服务中嵌入 Prometheus 指标暴露端点,使用 promhttp 和 prometheus 客户端库采集请求延迟直方图。
// 定义延迟直方图
var requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request latency in seconds.",
Buckets: prometheus.DefBuckets, // 默认桶:0.005, 0.01, 0.025, ..., 10
},
[]string{"method", "path", "status"},
)
// 初始化注册
func init() {
prometheus.MustRegister(requestDuration)
}
// 中间件采集
func latencyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
sw := &statusWriter{ResponseWriter: w}
next.ServeHTTP(sw, r)
duration := time.Since(start).Seconds()
requestDuration.With(prometheus.Labels{
"method": r.Method,
"path": r.URL.Path,
"status": strconv.Itoa(sw.status),
}).Observe(duration)
})
}
注意:直方图桶(Buckets)的分布直接影响 P99 计算精度。对于尾延迟诊断,建议自定义桶:
prometheus.ExponentialBuckets(0.001, 2, 15),覆盖 1ms ~ 16s 范围。
4.4 客户端延迟采集
客户端侧延迟数据通过 wrk2 的 --latency 参数输出,同时使用 tee 命令将原始延迟分布保存到文件,供后续离线分析。
# 压测命令示例
wrk2 -t 4 -c 40 -d 120s --rate 1000 --latency \
http://target-service:8080/api/search \
| tee wrk2_latency_$(date +%Y%m%d_%H%M%S).log
输出示例(关键行):
Latency Distribution (HdrHistogram - Recorded Latency)
50.000% 0.045s
90.000% 0.089s
99.000% 0.215s
99.900% 0.512s
99.990% 0.890s
99.999% 1.200s
100.000% 1.500s
4.5 Prometheus + Grafana 监控配置
4.5.1 Prometheus 抓取配置
# prometheus.yml
scrape_configs:
- job_name: 'go-service'
scrape_interval: 5s
scrape_timeout: 3s
static_configs:
- targets:
- 'node1:8080'
- 'node2:8081'
metrics_path: /metrics
- job_name: 'node-exporter'
scrape_interval: 10s
static_configs:
- targets:
- 'node1:9100'
- 'node2:9100'
4.5.2 Grafana 面板关键指标
| 面板 | PromQL 查询 | 说明 |
|---|---|---|
| P50 延迟 | histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[1m])) by (le)) |
中位数延迟 |
| P99 延迟 | histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[1m])) by (le)) |
尾延迟核心指标 |
| P99.9 延迟 | histogram_quantile(0.999, sum(rate(http_request_duration_seconds_bucket[1m])) by (le)) |
极端尾延迟 |
| QPS | sum(rate(http_request_duration_seconds_count[1m])) |
每秒请求数 |
| 错误率 | sum(rate(http_request_duration_seconds_count{status=~"5.."}[1m])) / sum(rate(http_request_duration_seconds_count[1m])) |
5xx 占比 |
4.6 数据采集验证流程
- 启动服务:在两个 Go 节点上启动服务,确认
/metrics端点返回数据。 - 启动监控:启动 Prometheus 和 Grafana,确认 target 状态为 UP。
- 预热压测:以 50% 目标 QPS 运行 30 秒,等待指标稳定。
- 正式压测:以目标 QPS 运行 120 秒,同时记录客户端和服务端延迟。
- 数据对齐:将 wrk2 输出的客户端延迟分布与 Grafana 面板上的服务端 P99 延迟进行对比,偏差应 < 5%。
验证标准:客户端 P99 延迟与服务端 P99 延迟的差值应小于 10ms,否则说明网络或负载均衡引入了额外抖动,需要排查。
4.7 常见问题与避坑
- 时间不同步:所有节点必须使用 NTP 同步时间,否则客户端和服务端延迟无法对齐。
- 直方图桶溢出:如果请求延迟超过最大桶值(默认 10s),Prometheus 会将其计入
+Inf桶,导致 P99 计算失真。务必根据实际延迟范围调整桶。 - 压测客户端瓶颈:wrk2 本身可能成为瓶颈。使用
top监控客户端 CPU,若接近 100%,需增加客户端节点或降低并发线程数。 - Grafana 刷新频率:面板刷新间隔建议设为 5s,与 Prometheus 抓取间隔一致,避免数据空洞。