内容简介:2)程序主函数:Counter 一个累加指标数据,这个值随着时间只会逐渐的增加,比如程序完成的总任务数量,运行错误发生的总次数。常见的还有交换机中snmp采集的数据流量也属于该类型,代表了持续增加的数据包或者传输字节累加值。Gauge代表了采集的一个单一数据,这个数据可以增加也可以减少,比如CPU使用情况,内存使用量,硬盘当前的空间容量等等
-
保证 Go 语言安装环境(1.7以上)
1) 下载对应的prometheus包
go get github.com/prometheus/client_golang/prometheus/promhttp
2)程序主函数:
package main import ( "log" "net/http" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":8080", nil)) }
-
指标类别
Prometheus中主要使用的四类指标类型,如下所示
- Counter (累加指标)
- Gauge (测量指标)
- Summary (概略图)
- Histogram (直方图)
Counter 一个累加指标数据,这个值随着时间只会逐渐的增加,比如程序完成的总任务数量,运行错误发生的总次数。常见的还有交换机中snmp采集的数据流量也属于该类型,代表了持续增加的数据包或者传输字节累加值。
Gauge代表了采集的一个单一数据,这个数据可以增加也可以减少,比如CPU使用情况,内存使用量,硬盘当前的空间容量等等
Histogram和Summary使用的频率较少,两种都是基于采样的方式。另外有一些库对于这两个指标的使用和支持程度不同,有些仅仅实现了部分功能。这两个类型对于某一些业务需求可能比较常见,比如查询单位时间内:总的响应时间低于300ms的占比,或者查询95%用户查询的门限值对应的响应时间是多少。 使用Histogram和Summary指标的时候同时会产生多组数据,_count代表了采样的总数,_sum则代表采样值的和。 _bucket则代表了落入此范围的数据。
下面是使用historam来定义的一组指标,计算出了平均五分钟内的查询请求小于0.3s的请求占比总量的比例值。
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job) / sum(rate(http_request_duration_seconds_count[5m])) by (job)
如果需要聚合数据,可以使用histogram. 并且如果对于分布范围有明确的值的情况下(比如300ms),也可以使用histogram。但是如果仅仅是一个百分比的值(比如上面的95%),则使用Summary
-
定义指标
这里我们需要引入另一个依赖库
go get github.com/prometheus/client_golang/prometheus
下面先来定义了两个指标数据,一个是Guage类型, 一个是Counter类型。分别代表了CPU温度和磁盘失败次数统计,使用上面的定义进行分类。
cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "cpu_temperature_celsius", Help: "Current temperature of the CPU.", }) hdFailures = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "hd_errors_total", Help: "Number of hard-disk errors.", }, []string{"device"}, )
这里还可以注册其他的参数,比如上面的磁盘失败次数统计上,我们可以同时传递一个device设备名称进去,这样我们采集的时候就可以获得多个不同的指标。每个指标对应了一个设备的磁盘失败次数统计。
- 注册指标
func init() { // Metrics have to be registered to be exposed: prometheus.MustRegister(cpuTemp) prometheus.MustRegister(hdFailures) }
使用prometheus.MustRegister是将数据直接注册到Default Registry,就像上面的运行的例子一样,这个Default Registry不需要额外的任何代码就可以将指标传递出去。注册后既可以在程序层面上去使用该指标了,这里我们使用之前定义的指标提供的API(Set和With().Inc)去改变指标的数据内容
func main() { cpuTemp.Set(65.3) hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc() // The Handler function provides a default handler to expose metrics // via an HTTP server. "/metrics" is the usual endpoint for that. http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":8080", nil)) }
其中With函数是传递到之前定义的label=”device”上的值,也就是生成指标类似于
cpu_temperature_celsius 65.3 hd_errors_total{"device"="/dev/sda"} 1
当然我们写在main函数中的方式是有问题的,这样这个指标仅仅改变了一次,不会随着我们下次采集数据的时候发生任何变化,我们希望的是每次执行采集的时候,程序都去自动的抓取指标并将数据通过http的方式传递给我们。
-
Counter数据采集实例
下面是一个采集Counter类型数据的实例,这个例子中实现了一个自定义的,满足采集器(Collector)接口的结构体,并手动注册该结构体后,使其每次查询的时候自动执行采集任务。
我们先来看下采集器Collector接口的实现
type Collector interface { // 用于传递所有可能的指标的定义描述符 // 可以在程序运行期间添加新的描述,收集新的指标信息 // 重复的描述符将被忽略。两个不同的Collector不要设置相同的描述符 Describe(chan<- *Desc) // Prometheus的注册器调用Collect执行实际的抓取参数的工作, // 并将收集的数据传递到Channel中返回 // 收集的指标信息来自于Describe中传递,可以并发的执行抓取工作,但是必须要保证线程的安全。 Collect(chan<- Metric) }
了解了接口的实现后,我们就可以写自己的实现了,先定义结构体,这是一个集群的指标采集器,每个集群都有自己的Zone,代表集群的名称。另外两个是保存的采集的指标。
type ClusterManager struct { Zone string OOMCountDesc *prometheus.Desc RAMUsageDesc *prometheus.Desc }
我们来实现一个采集工作,放到了ReallyExpensiveAssessmentOfTheSystemState函数中实现,每次执行的时候,返回一个按照主机名作为键采集到的数据,两个返回值分别代表了OOM错误计数,和RAM使用指标信息。
func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() ( oomCountByHost map[string]int, ramUsageByHost map[string]float64, ) { oomCountByHost = map[string]int{ "foo.example.org": int(rand.Int31n(1000)), "bar.example.org": int(rand.Int31n(1000)), } ramUsageByHost = map[string]float64{ "foo.example.org": rand.Float64() * 100, "bar.example.org": rand.Float64() * 100, } return }
实现Describe接口,传递指标描述符到channel
// Describe simply sends the two Descs in the struct to the channel. func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) { ch <- c.OOMCountDesc ch <- c.RAMUsageDesc }
Collect函数将执行抓取函数并返回数据,返回的数据传递到channel中,并且传递的同时绑定原先的指标描述符。以及指标的类型(一个Counter和一个Guage)
func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) { oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState() for host, oomCount := range oomCountByHost { ch <- prometheus.MustNewConstMetric( c.OOMCountDesc, prometheus.CounterValue, float64(oomCount), host, ) } for host, ramUsage := range ramUsageByHost { ch <- prometheus.MustNewConstMetric( c.RAMUsageDesc, prometheus.GaugeValue, ramUsage, host, ) } }
创建结构体及对应的指标信息,NewDesc参数第一个为指标的名称,第二个为帮助信息,显示在指标的上面作为注释,第三个是定义的label名称数组,第四个是定义的Labels
func NewClusterManager(zone string) *ClusterManager { return &ClusterManager{ Zone: zone, OOMCountDesc: prometheus.NewDesc( "clustermanager_oom_crashes_total", "Number of OOM crashes.", []string{"host"}, prometheus.Labels{"zone": zone}, ), RAMUsageDesc: prometheus.NewDesc( "clustermanager_ram_usage_bytes", "RAM usage as reported to the cluster manager.", []string{"host"}, prometheus.Labels{"zone": zone}, ), } }
执行主程序
func main() { workerDB := NewClusterManager("db") workerCA := NewClusterManager("ca") // Since we are dealing with custom Collector implementations, it might // be a good idea to try it out with a pedantic registry. reg := prometheus.NewPedanticRegistry() reg.MustRegister(workerDB) reg.MustRegister(workerCA) }
如果直接执行上面的参数的话,不会获取任何的参数,因为程序将自动推出,我们并未定义http接口去暴露数据出来,因此数据在执行的时候还需要定义一个httphandler来处理http请求。
添加下面的代码到main函数后面,即可实现数据传递到http接口上:
gatherers := prometheus.Gatherers{ prometheus.DefaultGatherer, reg, } h := promhttp.HandlerFor(gatherers, promhttp.HandlerOpts{ ErrorLog: log.NewErrorLogger(), ErrorHandling: promhttp.ContinueOnError, }) http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { h.ServeHTTP(w, r) }) log.Infoln("Start server at :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Errorf("Error occur when start server %v", err) os.Exit(1) }
其中prometheus.Gatherers用来定义一个采集数据的收集器集合,可以merge多个不同的采集数据到一个结果集合,这里我们传递了缺省的DefaultGatherer,所以他在输出中也会包含go运行时指标信息。同时包含reg是我们之前生成的一个注册对象,用来自定义采集数据。
promhttp.HandlerFor()函数传递之前的Gatherers对象,并返回一个httpHandler对象,这个httpHandler对象可以调用其自身的ServHTTP函数来接手http请求,并返回响应。其中promhttp.HandlerOpts定义了采集过程中如果发生错误时,继续采集其他的数据。
尝试刷新几次浏览器获取最新的指标信息
clustermanager_oom_crashes_total{host="bar.example.org",zone="ca"} 364 clustermanager_oom_crashes_total{host="bar.example.org",zone="db"} 90 clustermanager_oom_crashes_total{host="foo.example.org",zone="ca"} 844 clustermanager_oom_crashes_total{host="foo.example.org",zone="db"} 801 # HELP clustermanager_ram_usage_bytes RAM usage as reported to the cluster manager. # TYPE clustermanager_ram_usage_bytes gauge clustermanager_ram_usage_bytes{host="bar.example.org",zone="ca"} 10.738111282075208 clustermanager_ram_usage_bytes{host="bar.example.org",zone="db"} 19.003276633920805 clustermanager_ram_usage_bytes{host="foo.example.org",zone="ca"} 79.72085409108028 clustermanager_ram_usage_bytes{host="foo.example.org",zone="db"} 13.041384617379178
每次刷新的时候,我们都会获得不同的数据,类似于实现了一个数值不断改变的采集器。当然,具体的指标和采集函数还需要按照需求进行修改,满足实际的业务需求。
-
添加自己的时间戳(NewMetricWithTimestamp)
因为业务需要,数据上报需要严格按照自己的时间戳进行数据的处理
desc := prometheus.NewDesc( "temperature_kelvin", "Current temperature in Kelvin.", nil, nil, ) // Create a constant gauge from values we got from an external // temperature reporting system. Those values are reported with a slight // delay, so we want to add the timestamp of the actual measurement. temperatureReportedByExternalSystem := 298.15 timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC) s := prometheus.NewMetricWithTimestamp( timeReportedByExternalSystem, prometheus.MustNewConstMetric( desc, prometheus.GaugeValue, temperatureReportedByExternalSystem, ), ) // Just for demonstration, let's check the state of the gauge by // (ab)using its Write method (which is usually only used by Prometheus // internally). metric := &dto.Metric{} s.Write(metric) fmt.Println(proto.MarshalTextString(metric))
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 基于顺丰同城接口编写sdk,java三方sdk编写思路
- 使用 Clojure 编写 OpenWhisk 操作,第 1 部分: 使用 Lisp 方言为 OpenWhisk 编写简明的代码
- 编写一个Locust文件
- 编写现代 JavaScript 代码
- 性能测试报告编写技巧
- 为vscode编写扩展
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。