go-restful接入prometheus笔记
文章目录
-
- 一、前言
- 二、接入prometheus
-
- 1、确认集群pod是否支持prometheus以及上传指标的地址
-
- (1)查询集群下的prometheus 上报地址列表
- (2)根据pod的ip进行搜索
- 2、下载扩展包
- 3、上报prometheus的步骤
-
- (1)定义指标和注册指标
- (2)启动http服务和prometheus的handler
- (3)报指标启动服务,查看指标注册情况
-
- 1)代码中查看指标注册情况
- 2)通过接口查看
- 三、启动http服务的问题
-
- 1、启动两个http服务
- 2、启动单个http服务
- 四、关于prometheus的其他问题
-
- 1、注册的指标,和http怎么联系起来的呢
- 2、Register和MustRegister的区别
- 五、完整上报prometheus指标的demo
-
- 1、初始化和定义指标
- 2、初始化和上报指标
一、前言
云服务时代,接入prometheus
是再寻常不过的事情了,只不过之前的接入或多或少都是基于的公司基建,轻松且简单。本次算是从头接了一遍prometheus
,还是有些有意思的点的,记录一下。
二、接入prometheus
1、确认集群pod是否支持prometheus以及上传指标的地址
(1)查询集群下的prometheus 上报地址列表
http://集群ip:9090/targets
注:Prometheus
的 /targets
端点用于显示 Prometheus
抓取器的目标列表,即正在被监视的数据源。该端点显示了所有已配置的目标及其当前状态,包括最后一次抓取时间、抓取结果、抓取错误等信息。
(2)根据pod的ip进行搜索
比如服务pod
节点ip
是:10.xx.x.63
,在targets
列表中搜索该ip
,可以看到prometheus
采集的地址是:
https://10.xx.x.63:10250/metrics
prometheus
的采集端口10250
是默认端口。服务层面不需要关心,只需要在自己的http
服务基础上,暴露一个/metrics
接口即可。接口处理的handler
由prometheus
处理。
如果集群中还没有接入prometheus
的收集器,那么只能去找运维同学了。
2、下载扩展包
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promauto
go get github.com/prometheus/client_golang/prometheus/promhttp
3、上报prometheus的步骤
(1)定义指标和注册指标
具体需要什么指标可以先查查各指标的含义,博主这里是使用Counter
计数器指标和Histogram
指标统计请求耗时等。
以下为部分代码:
func NewMetrics() *Metrics {metrics := &Metrics{counters: make(map[string]*prometheus.CounterVec),gauges: make(map[string]*prometheus.GaugeVec),histograms: make(map[string]*prometheus.HistogramVec),}
func (m *Metrics) Register() {// 初始化 prometheus, 将所有计数器转换为 CounterVec 类型,并注册到 prometheusfor name, counter := range m.counters {Logger.Warnf("Register Counter metrics for:%s!", name)prometheus.MustRegister(counter)}}
(2)启动http服务和prometheus的handler
http.Handle("/metrics", promhttp.Handler())
promhttp.Handler()
用于创建一个 http
处理程序,该处理程序返回所有已注册的Prometheus
指标。所以在启动http
服务之前,必须要注册所有的指标。
(3)报指标启动服务,查看指标注册情况
// go-restful 获取定义的路由子路径
subPath := req.SelectedRoutePath()
common.MetricsInstance.ReportCounter(common.CoreRequestTotalCounter, 1, map[string]string{"api": subPath})
1)代码中查看指标注册情况
// 查看已注册的指标// 使用 DefaultGatherer 获取已经注册的指标数据gathered, err := prometheus.DefaultGatherer.Gather()if err != nil {panic(err)}for _, f := range gathered {// 输出指标数据Logger.Infof("查看注册的metrics", f.GetName(), f.GetHelp())}
2)通过接口查看
curl "http://127.0.0.1:8000/metrics"
// 返回很多默认指标,以及自定义的指标。
三、启动http服务的问题
关于go中的http服务参考:go http 服务器编程
1、启动两个http服务
restful走8000端口,handler是go-restful的handler.
prometheus走9090端口,handler是promhttp.Handler()// restful
srv := &http.Server{Handler: s.Container,Addr: 8000,WriteTimeout: 15 * time.Second,ReadTimeout: 15 * time.Second,}// restfulgo func() {if err := srv.ListenAndServe(); err != http.ErrServerClosed {Logger.Fatalf("HTTP server ListenAndServe: %v", err)}}()// register promhttpmetrics := http.NewServeMux()metrics.Handle("/metrics", promhttp.Handler())metricsServer := &http.Server{Addr: ":9090",Handler: metrics,}go metricsServer.ListenAndServe()Logger.Infof("HTTP metrics started at %s", "9090")
开启两个http
服务一般是因为端口不一致所以才开启的,一般来说还是跟go-restful
端口保持一致比较好。
2、启动单个http服务
//ServeMux 可以注册多了 URL 和 handler 的对应关系,并自动把请求转发到对应的 handler 进行处理。//以/ 结尾的 URL 可以匹配它的任何子路径,比如 /images 会匹配 /images/cute-cat.jpgmux := http.NewServeMux()mux.Handle("/", s.Container)mux.Handle("/metrics", promhttp.Handler())srv := &http.Server{Handler: mux,Addr: 8000,WriteTimeout: 15 * time.Second,ReadTimeout: 15 * time.Second,}go func() {if err := srv.ListenAndServe(); err != http.ErrServerClosed {Logger.Fatalf("HTTP server ListenAndServe: %v", err)}}()
这样原先的接口访问不受影响。但是访问/metrics
的时候,就会走到prometheus
的handler
,从而读取到当前注册的所有指标。
四、关于prometheus的其他问题
1、注册的指标,和http怎么联系起来的呢
在 Prometheus
中,Registry
对象是一个全局的管理器,用于注册和存储所有的指标对象。Prometheus
的 HTTP 服务器将使用此 Registry
对象来提供 /metrics
端点,以便其他应用程序和监控系统可以获取指标数据。
Register()
函数将指标对象注册到 Registry
对象中,当调用 promhttp.Handler()
函数时,将会创建一个新的 HTTP
处理程序对象,并使用默认的Registry
和 Gatherer
实例来管理和读取所有的指标数据。
该处理程序对象将在收到 HTTP
请求时调用 Gatherer
对象的 Gather()
方法,从而读取当前所有的指标,并将它们作为响应内容返回给客户端。
2、Register和MustRegister的区别
prometheus.Register(counter): 将指标对象注册到 registry 中。该方法会返回一个 error,表示注册时可能出现的错误,如果发生错误则会将其忽略,并且不会在控制台输出任何信息。
prometheus.MustRegister(counter): 将指标对象注册到 registry 中。该方法不会返回任何值,如果出现错误则会直接 panic,并将错误信息输出到控制台。通常用于初始化阶段,如果无法正确注册指标对象,则终止程序运行。
五、完整上报prometheus指标的demo
1、初始化和定义指标
package commonimport ("sync""github.com/prometheus/client_golang/prometheus"
)var metricsOnce sync.Once
var MetricsInstance *Metrics// 定义指标
const (HttpRequestTotalCounter = "http_requests_total"CoreRequestTotalCounter = "core_requests_total"CoreRequestSuccessTotalCounter = "core_requests_success_total"CoreRequestCostHistogram = "core_request_cost"
)type Metrics struct {counters map[string]*prometheus.CounterVecgauges map[string]*prometheus.GaugeVechistograms map[string]*prometheus.HistogramVecmu sync.RWMutex
}// prometheus的指标必须在http服务启动之前定义好
func NewMetrics() *Metrics {metrics := &Metrics{counters: make(map[string]*prometheus.CounterVec),gauges: make(map[string]*prometheus.GaugeVec),histograms: make(map[string]*prometheus.HistogramVec),}// 创建并注册 Counter 指标// 请求总数metrics.counters[HttpRequestTotalCounter] = prometheus.NewCounterVec(prometheus.CounterOpts{Name: HttpRequestTotalCounter,Help: "The total number of HTTP requests",}, []string{"api"})// 核心功能请求数metrics.counters[CoreRequestTotalCounter] = prometheus.NewCounterVec(prometheus.CounterOpts{Name: CoreRequestTotalCounter,Help: "The total number of core function requests",}, []string{"api"})// 核心功能请求成功数metrics.counters[CoreRequestSuccessTotalCounter] = prometheus.NewCounterVec(prometheus.CounterOpts{Name: CoreRequestSuccessTotalCounter,Help: "The total number of core function success requests",}, []string{"api"})// histogram指标,核心功能请求耗时。 可以通过api标签,区分接口名称metrics.histograms[CoreRequestCostHistogram] = prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: CoreRequestCostHistogram,Help: "The total number of core apis request cost",Buckets: []float64{100, 200, 300, 400, 500, 600, 700, 800, 900, 1000},}, []string{"api"})metricsOnce.Do(func() {MetricsInstance = metrics})return MetricsInstance
}func (m *Metrics) Register() {// 初始化 prometheus, 将所有计数器转换为 CounterVec 类型,并注册到 prometheusfor name, counter := range m.counters {Logger.Warnf("Register Counter metrics for:%s!", name)prometheus.MustRegister(counter)}//将所有 Gauge 转换为 GaugeVec 类型,并注册到 prometheusfor name, gauge := range m.gauges {Logger.Warnf("Register gauges metrics for:%s!", name)prometheus.MustRegister(gauge)}// 将所有 Histogram 转换为 HistogramVec 类型,并注册到 prometheusfor name, histogram := range m.histograms {Logger.Warnf("Register histograms metrics for:%s!", name)prometheus.MustRegister(histogram)}// 查看已注册的指标// 使用 DefaultGatherer 获取已经注册的指标数据gathered, err := prometheus.DefaultGatherer.Gather()if err != nil {panic(err)}for _, f := range gathered {// 输出指标数据Logger.Infof("查看注册的metrics", f.GetName(), f.GetHelp())}
}// ReportCounter with labels,example:
func (m *Metrics) ReportCounter(name string, value float64, labels map[string]string) {m.mu.Lock()defer m.mu.Unlock()if _, ok := m.counters[name]; !ok {Logger.Warnf("Counter metrics for:%s not exist!", name)return}Logger.Infof("Counter Add for name:%s", name)m.counters[name].With(labels).Add(value)
}func (m *Metrics) ReportGauge(name string, value float64, labels map[string]string) {m.mu.Lock()defer m.mu.Unlock()if _, ok := m.gauges[name]; !ok {Logger.Infof("Gauge metrics for:%s not exist!", name)return}m.gauges[name].With(labels).Set(value)
}func (m *Metrics) ReportHistogram(name string, value float64, labels map[string]string) {m.mu.Lock()defer m.mu.Unlock()if _, ok := m.histograms[name]; !ok {Logger.Infof("Histogram metrics for:%s not exist!", name)}m.histograms[name].With(labels).Observe(value)
}
2、初始化和上报指标
// 初始化
metrics := common.NewMetrics()
metrics.Register()//上报指标
subPath := req.SelectedRoutePath()common.MetricsInstance.ReportCounter(common.CoreRequestTotalCounter, 1, map[string]string{"api": subPath})
end