4. 压测环境搭建与数据采集

本章聚焦于搭建一套可复现的压测环境,并配置端到端的延迟监控链路。目标是让服务端和客户端的延迟数据能够被精确采集、对齐和可视化,为后续的尾延迟诊断提供可靠的数据基础。

4.1 压测环境拓扑

采用三节点集群部署:1 台压测客户端 + 2 台 Go 服务节点(负载均衡)。所有节点位于同一内网,避免网络抖动干扰。

环境规格:CPU 4 核 / 内存 8 GB / 磁盘 SSD / 内核参数 net.core.somaxconn=65535 / 关闭 swap。
压测环境拓扑 压测客户端 wrk2 / ghz 负载均衡 nginx / L4 转发 Go 服务节点 1 :8080 Go 服务节点 2 :8081 监控栈 Prometheus Grafana Node Exporter

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 数据采集验证流程

  1. 启动服务:在两个 Go 节点上启动服务,确认 /metrics 端点返回数据。
  2. 启动监控:启动 Prometheus 和 Grafana,确认 target 状态为 UP。
  3. 预热压测:以 50% 目标 QPS 运行 30 秒,等待指标稳定。
  4. 正式压测:以目标 QPS 运行 120 秒,同时记录客户端和服务端延迟。
  5. 数据对齐:将 wrk2 输出的客户端延迟分布与 Grafana 面板上的服务端 P99 延迟进行对比,偏差应 < 5%。
验证标准:客户端 P99 延迟与服务端 P99 延迟的差值应小于 10ms,否则说明网络或负载均衡引入了额外抖动,需要排查。

4.7 常见问题与避坑

  • 时间不同步:所有节点必须使用 NTP 同步时间,否则客户端和服务端延迟无法对齐。
  • 直方图桶溢出:如果请求延迟超过最大桶值(默认 10s),Prometheus 会将其计入 +Inf 桶,导致 P99 计算失真。务必根据实际延迟范围调整桶。
  • 压测客户端瓶颈:wrk2 本身可能成为瓶颈。使用 top 监控客户端 CPU,若接近 100%,需增加客户端节点或降低并发线程数。
  • Grafana 刷新频率:面板刷新间隔建议设为 5s,与 Prometheus 抓取间隔一致,避免数据空洞。