赞
踩
云服务时代,接入prometheus
是再寻常不过的事情了,只不过之前的接入或多或少都是基于的公司基建,轻松且简单。本次算是从头接了一遍prometheus
,还是有些有意思的点的,记录一下。
http://集群ip:9090/targets
注:Prometheus
的 /targets
端点用于显示 Prometheus
抓取器的目标列表,即正在被监视的数据源。该端点显示了所有已配置的目标及其当前状态,包括最后一次抓取时间、抓取结果、抓取错误等信息。
比如服务pod
节点ip
是:10.xx.x.63
,在targets
列表中搜索该ip
,可以看到prometheus
采集的地址是:
https://10.xx.x.63:10250/metrics
prometheus
的采集端口10250
是默认端口。服务层面不需要关心,只需要在自己的http
服务基础上,暴露一个/metrics
接口即可。接口处理的handler
由prometheus
处理。
如果集群中还没有接入prometheus
的收集器,那么只能去找运维同学了。
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
具体需要什么指标可以先查查各指标的含义,博主这里是使用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 类型,并注册到 prometheus for name, counter := range m.counters { Logger.Warnf("Register Counter metrics for:%s!", name) prometheus.MustRegister(counter) } }
http.Handle("/metrics", promhttp.Handler())
promhttp.Handler()
用于创建一个 http
处理程序,该处理程序返回所有已注册的Prometheus
指标。所以在启动http
服务之前,必须要注册所有的指标。
// go-restful 获取定义的路由子路径
subPath := req.SelectedRoutePath()
common.MetricsInstance.ReportCounter(common.CoreRequestTotalCounter, 1, map[string]string{"api": subPath})
// 查看已注册的指标
// 使用 DefaultGatherer 获取已经注册的指标数据
gathered, err := prometheus.DefaultGatherer.Gather()
if err != nil {
panic(err)
}
for _, f := range gathered {
// 输出指标数据
Logger.Infof("查看注册的metrics", f.GetName(), f.GetHelp())
}
curl "http://127.0.0.1:8000/metrics"
// 返回很多默认指标,以及自定义的指标。
关于go中的http服务参考:go http 服务器编程
restful走8000端口,handler是go-restful的handler. prometheus走9000端口,handler是promhttp.Handler() // restful srv := &http.Server{ Handler: s.Container, Addr: 8000, WriteTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second, } // restful go func() { if err := srv.ListenAndServe(); err != http.ErrServerClosed { Logger.Fatalf("HTTP server ListenAndServe: %v", err) } }() // register promhttp metrics := http.NewServeMux() metrics.Handle("/metrics", promhttp.Handler()) metricsServer := &http.Server{ Addr: ":9000", Handler: metrics, } go metricsServer.ListenAndServe() Logger.Infof("HTTP metrics started at %s", "9090")
开启两个http
服务一般是为了不影响业务服务,除了业务的8080
端口,再开一个9000
端口用来上报prometheus
指标和健康检查,或者接入其他的第三方组件。
//ServeMux 可以注册多了 URL 和 handler 的对应关系,并自动把请求转发到对应的 handler 进行处理。 //以/ 结尾的 URL 可以匹配它的任何子路径,比如 /images 会匹配 /images/cute-cat.jpg mux := 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
中,Registry
对象是一个全局的管理器,用于注册和存储所有的指标对象。Prometheus
的 HTTP 服务器将使用此 Registry
对象来提供 /metrics
端点,以便其他应用程序和监控系统可以获取指标数据。
Register()
函数将指标对象注册到 Registry
对象中,当调用 promhttp.Handler()
函数时,将会创建一个新的 HTTP
处理程序对象,并使用默认的Registry
和 Gatherer
实例来管理和读取所有的指标数据。
该处理程序对象将在收到 HTTP
请求时调用 Gatherer
对象的 Gather()
方法,从而读取当前所有的指标,并将它们作为响应内容返回给客户端。
prometheus.Register(counter): 将指标对象注册到 registry 中。该方法会返回一个 error,表示注册时可能出现的错误,如果发生错误则会将其忽略,并且不会在控制台输出任何信息。
prometheus.MustRegister(counter): 将指标对象注册到 registry 中。该方法不会返回任何值,如果出现错误则会直接 panic,并将错误信息输出到控制台。通常用于初始化阶段,如果无法正确注册指标对象,则终止程序运行。
package common import ( "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.CounterVec gauges map[string]*prometheus.GaugeVec histograms map[string]*prometheus.HistogramVec mu 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 类型,并注册到 prometheus for name, counter := range m.counters { Logger.Warnf("Register Counter metrics for:%s!", name) prometheus.MustRegister(counter) } //将所有 Gauge 转换为 GaugeVec 类型,并注册到 prometheus for name, gauge := range m.gauges { Logger.Warnf("Register gauges metrics for:%s!", name) prometheus.MustRegister(gauge) } // 将所有 Histogram 转换为 HistogramVec 类型,并注册到 prometheus for 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) }
// 初始化
metrics := common.NewMetrics()
metrics.Register()
//上报指标
subPath := req.SelectedRoutePath()
common.MetricsInstance.ReportCounter(common.CoreRequestTotalCounter, 1, map[string]string{"api": subPath})
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。