Chapter 06

应用埋点

用官方 client library 给自己的服务加业务指标:暴露 /metrics 端点,实践三种指标类型的埋点,处理短任务的 Pushgateway。

6.1 client library 概览

Prometheus 官方与社区提供各语言客户端库,负责注册指标、维护值、按格式渲染 /metrics

Registry 注册表
所有指标注册到一个 registry;默认 registry 自带进程指标(process_*go_*)。
Collector 采集器
能被 registry 收集的对象。Counter/Gauge/Histogram 都是 Collector。
Vec 向量型指标
带 label 的指标用 *Vec(如 CounterVec),调用 .WithLabelValues("GET","200") 拿到具体序列句柄。
Exposition Format 暴露格式
/metrics 返回的文本格式(含 # HELP# TYPE 注释行);新一代为 OpenMetrics。
语言
Gogithub.com/prometheus/client_golang
Pythonprometheus_client
Javaio.prometheus / micrometer
Node.jsprom-client
Rustprometheus / metrics

6.2 Go 埋点

package main

import (
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    reqTotal = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "HTTP 请求总数",
    }, []string{"method", "status"})

    reqDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "请求耗时",
        Buckets: prometheus.DefBuckets, // .005 ~ 10s
    }, []string{"method"})
)

func handler(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    // ... 业务逻辑 ...
    w.WriteHeader(200)
    reqTotal.WithLabelValues(r.Method, "200").Inc()
    reqDuration.WithLabelValues(r.Method).Observe(time.Since(start).Seconds())
}

func main() {
    http.HandleFunc("/", handler)
    http.Handle("/metrics", promhttp.Handler()) // 暴露端点
    http.ListenAndServe(":8080", nil)
}

6.3 Python 埋点

from prometheus_client import Counter, Gauge, Histogram, start_http_server
import time, random

REQ = Counter("tasks_processed_total", "处理任务数", ["type"])
INFLIGHT = Gauge("tasks_inflight", "处理中的任务")
LATENCY = Histogram("task_duration_seconds", "任务耗时")

@LATENCY.time()          # 装饰器自动记录耗时到 Histogram
def process(kind):
    INFLIGHT.inc()
    time.sleep(random.random())
    REQ.labels(type=kind).inc()
    INFLIGHT.dec()

if __name__ == "__main__":
    start_http_server(8000)   # /metrics on :8000
    while True:
        process("email")

6.4 埋点的黄金信号

不知道埋什么?从 Google SRE 的 四大黄金信号入手:

Latency 延迟
请求耗时,用 Histogram,区分成功/失败请求的延迟。
Traffic 流量
QPS、吞吐量,用 Counter + rate。
Errors 错误
失败率,用带 status 标签的 Counter。
Saturation 饱和度
资源用满程度(队列长度、连接池使用),用 Gauge。
RED 与 USE 方法论

服务监控用 RED(Rate 请求率 / Errors 错误 / Duration 延迟);资源监控用 USE(Utilization 使用率 / Saturation 饱和 / Errors 错误)。埋点时对号入座即可。

6.5 Pushgateway:短任务的中转站

Cron 任务、批处理跑完就退出,Prometheus 来抓时它已经没了。Pushgateway 让这些短任务把指标 push 上去暂存,等 Prometheus 来拉。

# 批处理任务结束时推送(bash + curl)
cat <<EOF | curl --data-binary @- \
  http://pushgateway:9091/metrics/job/backup/instance/db01
# TYPE backup_duration_seconds gauge
backup_duration_seconds 42.3
# TYPE backup_records_total counter
backup_records_total 128374
EOF
Pushgateway 不是万金油

只用于短生命周期批任务!它不做目标存活探测(推上去的值会一直留着直到被覆盖/删除),也不适合常驻服务。常驻服务应始终用 pull。用错了会导致陈旧数据长期误导告警。

6.6 验证埋点

$ curl -s localhost:8080/metrics | grep http_requests_total
# HELP http_requests_total HTTP 请求总数
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 42

然后在 prometheus.yml 里把这个 job 加进 scrape_configs,去表达式浏览器确认能查到即可。

6.7 小结与下一步