0%

Golang 应用集成 Prometheus 统计数据支持

基本概念

Prometheus 所有采集的监控数据均以指标(metric)的形式保存在内置的时间序列数据库当中(TSDB):属于同一指标名称,同一标签集合的、有时间戳标记的数据流。除了存储的时间序列,Prometheus 还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。

样本在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:

指标(metric):指标名称和描述当前样本特征的 labelsets;

时间戳(timestamp):一个精确到毫秒的时间戳;

样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。

1
<metric name>{<label name>=<label value>, ...}

Metric类型

Prometheus 客户端公开了在暴露服务指标时能够运用的四种指标类型。查看 Prometheus 的文档 以获得关于各种指标类型的深入信息。

Counter(计数器

一种累加的 metric,典型的应用如:请求的个数,结束的任务数,出现的错误数等。随着客户端不断请求,数值越来越大。例如api_http_requests_total{method="POST", handler="/messages"}

Gauge(计量器)

一种常规的 metric,典型的应用如:温度,运行的 goroutines 的个数。返回的数值会上下波动。
例如 go_goroutines{instance="172.17.0.2", job="Prometheus"}

Histogram(分布图)

可以理解为柱状图,典型的应用如:请求持续时间,响应大小。可以对观察结果采样,分组及统计。例如设置一个name为web_request_duration_seconds的Histogram 的metrics,并设置区间值为[0.1,0.5,1]会对区间点生成一条统计数据.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 响应时间小于0.1s的请求有5次
web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.1"} 3

# 响应时间小于0.5s的请求有次
web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.5"} 5

# 响应时间小于1s的请求有7次
web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="1"} 7

# 总共7次请求
web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="+Inf"} 7

# 7次请求duration的总和
web_request_duration_seconds_sum{endpoint="/query",method="GET"} 2.7190880529999997

Summary(摘要)

跟 histogram 类似,summary 也对观测值(类似请求延迟或回复包大小)进行采样。同时它会给出一个总数以及所有观测值的总和,它在一个滑动的时间窗口上计算可配置的分位数。, 典型的应用如:请求持续时间,响应大小。主要做统计用,设置分位数的值,会实时返回该分位数上的值。

Golang中集成

Prometheus 程序库 提供了一个用 Golang 写成的健壮的插桩库,可以用来注册,收集和暴露服务的指标。在讲述如何在应用程序中暴露指标前,让我们先来探究一下 Prometheus 库提供的各种指标类型。

  1. 下载 Prometheus go 客户端 go get github.com/prometheus/client_golang/prometheus/promhttp.

  2. Golang应用中集成 Prometheus HTTP 服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package main

    import (
    "net/http"

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

    func main() {
    // expose prometheus metrics接口

    server := http.NewServeMux() // create a new mux server
    server.Handle("/metrics", promhttp.Handler()) // register a new handler for the /metrics endpoint
    log.Fatal(http.ListenAndServe(":8080", server)) // start an http server using the mux server
    }
  3. 启动应用访问http://localhost:8080/metrics,会返回一些go应用中goroutine,thread、gc、堆栈的基本信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    # HELP go_gc_duration_seconds A summary of the GC invocation durations.

    # TYPE go_gc_duration_seconds summary

    go_gc_duration_seconds{quantile="0"} 0

    go_gc_duration_seconds{quantile="0.25"} 0

    go_gc_duration_seconds{quantile="0.5"} 0

    go_gc_duration_seconds{quantile="0.75"} 0

    go_gc_duration_seconds{quantile="1"} 0

    go_gc_duration_seconds_sum 0

    go_gc_duration_seconds_count 0

    # HELP go_goroutines Number of goroutines that currently exist.

    # TYPE go_goroutines gauge

    go_goroutines 6

    # HELP go_info Information about the Go environment.

    # TYPE go_info gauge

    go_info{version="go1.12"} 1

添加自定义指标

  1. 创建指标并注册到prometheus
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 初始化 web_reqeust_total, counter类型指标, 表示接收http请求总次数
    var WebRequestTotal = prometheus.NewCounterVec(
    prometheus.CounterOpts{
    Name: "web_reqeust_total",
    Help: "Number of hello requests in total",
    },
    // 设置两个标签 请求方法和 路径 对请求总次数在两个
    []string{"method", "endpoint"},
    )

    // web_request_duration_seconds,Histogram类型指标,bucket代表duration的分布区间
    var WebRequestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
    Name: "web_request_duration_seconds",
    Help: "web request duration distribution",
    Buckets: []float64{0.1, 0.3, 0.5, 0.7, 0.9, 1},
    },
    []string{"method", "endpoint"},
    )

    func init() {
    // 注册监控指标
    prometheus.MustRegister(WebRequestTotal)
    prometheus.MustRegister(WebRequestDuration)
    }
    1. 在适当切入点对指标进行记录变更
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      // 包装 handler function,不侵入业务逻辑
      func Monitor(h http.HandlerFunc) http.HandlerFunc {
      return func(w http.ResponseWriter, r *http.Request) {
      start := time.Now()
      h(w, r)
      duration := time.Since(start)
      // counter类型 metric的记录方式
      WebRequestTotal.With(prometheus.Labels{"method": r.Method, "endpoint": r.URL.Path}).Inc()
      // Histogram类型 meric的记录方式
      WebRequestDuration.With(prometheus.Labels{"method": r.Method, "endpoint": r.URL.Path}).Observe(duration.Seconds())
      }
      }

      ···

      func main() {
      // expose prometheus metrics接口
      http.Handle("/metrics", promhttp.Handler())
      http.HandleFunc("/query", monitor.Monitor(Query))
      log.Fatal(http.ListenAndServe(":8080", nil))
      }

      // query
      func Query(w http.ResponseWriter, r *http.Request) {
      //模拟业务查询耗时0~1s
      time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
      _,_ = io.WriteString(w, "some results")
      }
    2. 启动并访问http://localhost:8080/query接口,并再次访问http://localhost:8080/metrics接口,返回的指标数据中就有了刚加上的metric,如下所示
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      # HELP web_reqeust_total Number of hello requests in total
      # TYPE web_reqeust_total counter

      web_reqeust_total{endpoint="/hello",method="GET"} 1

      web_reqeust_total{endpoint="/query",method="GET"} 7

      # HELP web_request_duration_seconds web request duration distribution

      # TYPE web_request_duration_seconds histogram

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.1"} 3

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.3"} 3

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.5"} 5

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.7"} 5

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="0.9"} 7

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="1"} 7

      web_request_duration_seconds_bucket{endpoint="/query",method="GET",le="+Inf"} 7

      web_request_duration_seconds_sum{endpoint="/query",method="GET"} 2.7190880529999997

      web_request_duration_seconds_count{endpoint="/query",method="GET"} 7

代码地址

可以直接安装运行,详细说明可以参考官网

1
2
3
4
5
6
7
go install github.com/crockitwood/go-prometheus-example

go-prometheus-example

curl http://localhost:8080/query

curl http://localhost:8080/metrics