• Prometheus自定义Exporter的实现


    很多时候,我们在使用Prometheus时,官方提供的采集组件不能满足监控需求,我们就需要自行编写Exporter。

    本文的示例采用go语言和Gauge (测量指标)类型实现。自定义Exporter去取MongoDB里动态增长的数据。

    Metric接口

    Prometheus client库提供了四种度量标准类型。

    虽然只有基本度量标准类型实现Metric接口,但是度量标准及其向量版本都实现了Collector接口。Collector管理许多度量标准的收集,但为方便起见,度量标准也可以"自行收集"。Prometheus数据模型的一个非常重要的部分是沿称为label的维度对样本进行划分,从而产生度量向量。基本类型是GaugeVec,CounterVec,SummaryVec和HistogramVec。注意,Gauge,Counter,Summary和Histogram本身是接口,而GaugeVec,CounterVec,SummaryVec和HistogramVec则不是接口。

    • Counter (累积)

      Counter一般表示一个单调递增的计数器。

    • Gauge (测量)

      Gauge一般用于表示可能动态变化的单个值,可能增大也可能减小。

    • Histogram (直方图)

    • Summary (概略图)

    注册指标并启动HTTP服务

    需要先引入这个Prometheus client库。通常metric endpoint使用http来暴露metric,通过http暴露metric的工具为promhttp子包,引入promhttp和net/http。

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

    Registry/Register/Gatherers及http服务

    func main() {
    	daystr := time.Now().Format("20060102")
    	logFile, err := os.Create("./exporter/log/" + daystr + ".txt")
    	defer logFile.Close()
    	if err != nil {
    		fmt.Printf("%v\n", err)
    		return
    	}
    	logger := log.New(logFile, "Prefix_", log.Ldate|log.Ltime|log.Lshortfile)
    	
    	//Registry和Register部分
    	reg := prometheus.NewPedanticRegistry()
    	reg.MustRegister(metrics.initCollector())    //metrics.initCollector()是collector初始化模块
    	//以下是官方文档定义
    	// Register registers a new Collector to be included in metrics
    	// collection. It returns an error if the descriptors provided by the
    	// Collector are invalid or if they — in combination with descriptors of
    	// already registered Collectors — do not fulfill the consistency and
    	// uniqueness criteria described in the documentation of metric.Desc.
    	//
    	// If the provided Collector is equal to a Collector already registered
    	// (which includes the case of re-registering the same Collector), the
    	// returned error is an instance of AlreadyRegisteredError, which
    	// contains the previously registered Collector.
    	//
    	// A Collector whose Describe method does not yield any Desc is treated
    	// as unchecked. Registration will always succeed. No check for
    	// re-registering (see previous paragraph) is performed. Thus, the
    	// caller is responsible for not double-registering the same unchecked
    	// Collector, and for providing a Collector that will not cause
    	// inconsistent metrics on collection. (This would lead to scrape
    	// errors.)
    
    	// MustRegister works like Register but registers any number of
    	// Collectors and panics upon the first registration that causes an
    	// error.
    
    	//Gatherers部分
    	gatherers := prometheus.Gatherers{
    		reg,
    	}
    	//以下是官方文档定义
    	// Gatherers is a slice of Gatherer instances that 
    	// implements the Gatherer interface itself.
    	// Its Gather method calls Gather on all Gatherers 
    	// in the slice in order and returns the merged results.
    	// Errors returned from the Gather calls are 
    	// all returned in a flattened MultiError.
    	// Duplicate and inconsistent Metrics are 
    	// skipped (first occurrence in slice order wins) 
    	// and reported in the returned error.
    
    	//注册http服务
    	h := promhttp.HandlerFor(gatherers,
    		promhttp.HandlerOpts{
    			ErrorHandling: promhttp.ContinueOnError,
    		})
    	http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
    		h.ServeHTTP(w, r)
    	})
    	log.Println("Start server at :8710")
    	logger.Printf("Start server at :8710")
    
    	if err := http.ListenAndServe(":8710", nil); err != nil {
    		log.Printf("Error occur when start server %v", err)
    		logger.Printf("Error occur when start server %v", err)
    		os.Exit(1)
    	}		
    }	
    

    Registry是为了根据Prometheus数据模型来确保所收集指标的一致性。如果注册的Collector与注册的Metrics不兼容或不一致,则返回错误。理想情况下,在注册时而不是在收集时检测到不一致。对注册的Collector的检测通常会在程序启动时被检测到,而对注册的Metrics的检测只会在抓取时发生,这就是Collector和Metrics必须向Registry描述自己的主要原因。

    Registry实现了Gatherer接口。 然后Gather方法的调用者可以某种方式公开收集的指标。通常度量是通过/metrics端点上的HTTP提供的。在上面的示例中就是这种情况。通过HTTP公开指标的工具位于promhttp子软件包中。

    NewPedanticRegistry可以避免由DefaultRegisterer施加的全局状态,可以同时使用多个注册表,以不同的方式公开不同的指标, 也可以将单独的注册表用于测试目的。

    实现Collector接口

    type Collector interface {
        // 用于传递所有可能的指标的定义描述符
        // 可以在程序运行期间添加新的描述,收集新的指标信息
        // 重复的描述符将被忽略。两个不同的Collector不要设置相同的描述符
        Describe(chan<- *Desc)
    
        // Prometheus的注册器调用Collect执行实际的抓取参数的工作,
        // 并将收集的数据传递到Channel中返回
        // 收集的指标信息来自于Describe中传递,可以并发的执行抓取工作,但是必须要保证线程的安全。
        Collect(chan<- Metric)
    }
    

    在另外的模块中编写Collector以及其初始化的代码。

    先定义一个结构体,指标使用的是 prometheus的Desc类型。

    type ProjectMetrics struct {
    	MetricsDescs []*prometheus.Desc
    }
    

    定义MetricsName和 MetricsHelp。

    var MetricsNameXXX = "XXX"
    var MetricsHelpXXX = "(XXX)"
    

    对自定义类型ProjectMetrics定义Describe方法和Collect方法来实现实现Collector接口。

    func (c *ProjectMetrics) Describe(ch chan<- *prometheus.Desc) {
    	len1 := len(c.MetricsDescs)
    	for i := 0; i < len1; i++ {
    		ch <- c.MetricsDescs[i]
    	}
    }
    
    func (c *ProjectMetrics) Collect(ch chan<- prometheus.Metric) {
    	start := time.Now()
    	nowUTC := start.UTC()
    	resp := mongodb.QueryAllData(nowUTC.Unix())
    	for _, v := range resp.Data1Tier {
    		item1 := v.Item1
    		fmt.Println("......................", isp)
    		ts := time.Unix(v.Clientutc, 0)
    		for _, v2 := range v.Data2Tier {
    			item2 := v2.Item2
    			item3 := v2.Item3
    			item4 := v2.Item4
    			tmp := prometheus.NewDesc(
    				MetricsNameXXX,
    				MetricsHelpXXX,
    				[]string{"Name"},
    				prometheus.Labels{"item1": item1, "item3": item3, "item4": item4},
    			)
    			ch <- prometheus.NewMetricWithTimestamp(
    				ts,
    				prometheus.MustNewConstMetric(
    					tmp,
    					prometheus.GaugeValue,
    					item2,
    					MetricsNameXXX,
    				),
    			)
    		}
    	}
    	eT := time.Since(start)
    	fmt.Printf("Project Metrics, Elapsed Time: %s, Date(UTC): %s\n", eT, start.UTC().Format("2006/01/02T15:04:05"))
    }
    

    初始化ProjectMetrics,

    func AddMetricsItem2() *ProjectMetrics {
    	var tmpMetricsDescs []*prometheus.Desc
    	resp := mongodb.QueryAllData(time.Now().UTC().Unix())
    	for _, v := range resp.Data1Tier {
    		item1 := v.Item1
    		for _, v2 := range v.Data2Tier {
    
    			item3 := v2.Item3
    			item4 := v2.Item4
    			tmp := prometheus.NewDesc(
    				MetricsNameLatency,
    				MetricsHelpLatency,
    				[]string{"Name"},
    				prometheus.Labels{"item1": item1, "item3": item3, "item4": item4},
    			)
    			tmpMetricsDescs = append(tmpMetricsDescs, tmp)
    		}
    	} //aws
    
    	api := &ProjectMetrics{MetricsDescs: tmpMetricsDescs}
    	return api
    }
    

    MongoDB中的数据,

    type SensorData struct {
    	Aaaa         string      `json:"aaaa"`
    	Item2        float64     `json:"item2"`
    	Item4            string      `json:"item4"`
    	Item3 string      `json:"item3"`
    	Bbbb          float64     `json:"bbbb"`
    	Cccc         string      `json:"cccc"`
    	Dddd         []CmdResult `json:"dddd"`
    	Eeee           string      `json:"eeee"`
    }
    
    type Sensor struct {
    	Item1         string `json:"item1"`
    	Clientutc   int64  `json:"clientutc"`
    	Data2Tier []SensorData
    }
    
    type AllData struct {
    	Data1Tier []Sensor
    }
    

    mongodb.QueryAllData()是另外从mongodb中拉取数据的模块,返回AllData类型数据。mongodb模块每三分钟去数据库里拉一次数据。

  • 相关阅读:
    【SHOI2002】百事世界杯之旅
    【LGOJ 3384】树链剖分
    [20191006机房测试] 括号序列
    [20191006机房测试] 矿石
    【SHOI2012】回家的路
    [20191005机房测试] Seat
    [20191005机房测试] Silhouette
    每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样
    fgets函数读取最后一行的时候为什么会重复
    c语言中返回的变量地址,其物理地址在?(刨根问底)
  • 原文地址:https://www.cnblogs.com/dongyuq1/p/13572477.html
Copyright © 2020-2023  润新知