go-Prometheus

70 阅读2分钟

1 概述

我们经常用Prometheus来采集一些业务数据,通常的做法是在项目里定义相关的label-value结构体,写入数据,然后Prometheus从 /metrics 接口中获取数据,写入时序数据库,然后生成各种图标或进行告警。

2 简单实现

package main

import (
   "flag"
   "log"
   "net/http"

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

var addr = flag.String("listen-address", ":8090", "The address to listen on for HTTP requests.")

func main() {
   flag.Parse()
   http.Handle("/metrics", promhttp.Handler())
   log.Fatal(http.ListenAndServe(*addr, nil))
}

启动服务后,调用 metrics接口

curl localhost:8090/metrics

#返回:

# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.
# 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 8
.
.
.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 0
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0

可以看到,所使用的库有一些默认指标。我们接下来增加自己想要的业务指标。

3 新增指标

package main

import (
   "flag"
   "fmt"
   "log"
   "math/rand"
   "net/http"
   "time"

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

var addr = flag.String("listen-address", ":8090", "The address to listen on for HTTP requests.")

var (
   // CityTemperatureGauge Gauge 是一种既可增加也可减少还可设置为指定值的类型,底层为float64。
   // 适用指标:并发请求数量、内存使用量、温度等数据。
   CityTemperatureGauge = prometheus.NewGaugeVec(
      prometheus.GaugeOpts{
         Name: "city_temperature",
         Help: "city temperature",
      },
      []string{"city", "temperature"},
   )
)

func main() {
   flag.Parse()
   
   // 注册新的collector
   prometheus.MustRegister(CityTemperatureGauge)
   
   // 往metrics里面写数据
   AppendDataToMetrics()

   http.Handle("/metrics", promhttp.Handler())

   log.Fatal(http.ListenAndServe(*addr, nil))
}

// 往metrics里面写数据
func AppendDataToMetrics() {
   cityList := []string{"北京", "上海", "广州"}
   rand.Seed(time.Now().UnixNano())
   for _, city := range cityList {
      randomFloat := rand.Float64()*(35-25) + 25
      randomFloat = float64(int(randomFloat*10)) / 10
      CityTemperatureGauge.WithLabelValues(city, fmt.Sprintf("%.1f", randomFloat)).Set(1)
   }
}

启动后,请求 metrics 接口:

# HELP city_temperature city temperature.
# TYPE city_temperature gauge
city_temperature{city="上海",temperature="27.2"} 1
city_temperature{city="北京",temperature="34.6"} 1
city_temperature{city="广州",temperature="31.5"} 1
... 其他默认数据忽略...

这样我们就简单实现了一个业务指标。

4 改写路由

由于metrics接口会返回全量的数据,有时候我们其实不需要那些系统的默认数据,此时可以通过改写路由的方式,来只获取我们所需的那部分数据。

func main() {
   flag.Parse()
    
   // new一个注册器
   reg := prometheus.NewRegistry()
   reg.MustRegister(CityTemperatureGauge)

   AppendDataToMetrics()

   http.Handle("/metrics", promhttp.Handler())
   // 用新路由获取到新注册器的数据
   http.Handle("/metrics/cityTemp", promhttp.HandlerFor(
      reg,
      promhttp.HandlerOpts{
         // Opt into OpenMetrics to support exemplars.
         EnableOpenMetrics: true,
      },
   ))
   
   log.Fatal(http.ListenAndServe(*addr, nil))
}

请求

curl localhost:8090/metrics # 只返回默认数据
curl localhost:8090/metrics/cityTemp # 只返回 city_temperature 数据