prometheus+exporter小测试:

1.golang中使用expoter

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

func main(){
  http.Handle("/metrics", promhttp.Handler())
}

2.访问expoter内的数据

curl -G "http://192.168.0.88:8080/metrics" > text.txt

数据的格式如下:

# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.14.6"} 1

也可以访问GZIP格式的数据:

curl -G -H "User-Agent: Prometheus/2.28.1" -H "Accept: application/openmetrics-text; version=0.0.1,text/plain;version=0.0.4;q=0.5,*/*;q=0.1" -H "Accept-Encoding: gzip" -H "X-Prometheus-Scrape-Timeout-Seconds: 10" "http://192.168.0.88:8080/metrics" > 1.txt.gz
gzip -d 1.txt.gz

GZIP方式仍然是文本,但是压缩比很大。测试发现GZIP格式的体积只有文本格式的 15% !

3.源码分析

有几个疑问:存在一个很大的expoter——
1.能不能分段拉取数据?
2.能不能使用二进制格式来缩小体积?
3.会不会在拉取的时候,造成内存暴涨?(在exporter的进程中构造好数据后再发送)

3.1 http handle

client_golang/prometheus/promhttp/http.go

  • 入口函数
func Handler() http.Handler {
	return InstrumentMetricHandler(
		prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
	)
}
  • 在HandlerFor函数中提供处理代码
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler{
  //...
  h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
     //HTTP回调的主要逻辑
  })
}
  • 根据请求header来决定输出什么格式:
		var contentType expfmt.Format
		if opts.EnableOpenMetrics {
			contentType = expfmt.NegotiateIncludingOpenMetrics(req.Header)
		} else {
			contentType = expfmt.Negotiate(req.Header)
		}
  • 从代码看来,只有三种格式,且都是文本格式(解释了第二个问题)
func Negotiate(h http.Header) Format {
	for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
		ver := ac.Params["version"]
		if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
			switch ac.Params["encoding"] {
			case "delimited":
				return FmtProtoDelim
			case "text":
				return FmtProtoText
			case "compact-text":
				return FmtProtoCompact
			}
		}
		if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
			return FmtText
		}
	}
	return FmtText
}
  • 输出部分的代码
		for _, mf := range mfs {
			if handleError(enc.Encode(mf)) {
				return
			}
		}

是一边读取一边输出的。

  • 解释了第三个问题:不会造成内存暴涨
  • 解释了第一个问题:逻辑上不支持分段去拉

展望:

1.尽量不要存在很大的expoter
2.可以把proxy配置到一个很大的expoter上,然后每次拉取只返回一部分metric
3.可以基于树来重新实现expoter的逻辑

posted on 2021-08-09 20:33  ahfuzhang  阅读(212)  评论(0编辑  收藏  举报