exporter.go

Node Exporter源码解析

https://github.com/prometheus/mysqld_exporter/blob/main/collector/exporter.go

这是一个用于收集MySQL指标并通过Prometheus进行导出的Go语程序段。以下是代码中重要部分的说明:

  1. 引入包:在头部引入了一些必要的Go包,包括context, sql, fmt, regexp等标准库,并且还引入了一些外部库,如kingpin/v2用于处理命令行参数,go-kit/log用于日志记录,go-sql-driver/mysql为MySQL数据库驱动,以及prometheus/client_golang/prometheus用于Prometheus指标管理。

  2. 常量和全局变量定义:定义了一些SQL查询、正则表达式以及Prometheus指标描述。

  3. Exporter结构体:定义了一个名为Exporter的结构体,它持有一些字段,如上下文ctx、日志记录器logger、数据源名称dsn以及一组实现接口Scraper的对象。

  4. New函数:该函数接收上下文、数据源名称、抓取器集合和日志记录器作为参数,并返回一个初始化后的Exporter实例。

  5. Describe和Collect方法:这两个方法使得Exporter满足prometheus.Collector接口。Describe定义了指标的描述信息,Collect则负责收集指标数据。

  6. scrape方法scrape方法控制整个抓取过程。首先,它打开到MySQL的连接,如果连接失败,则记录错误并返回0,表示服务不可用。如果连接成功,它会遍历scrapers集合中的每个抓取器,并为每一个抓取器启动一个goroutine来执行抓取操作。

  7. getTargetFromDsn和getMySQLVersion方法getTargetFromDsn方法从DSN(数据源名称)中获取目标地址。getMySQLVersion方法查询并解析MySQL的版本信息。

这段代码的主要作用是通过Prometheus来收集、报告MySQL服务器的各种运行指标,如服务器是否在运行(mysql_up),以及收集过程是否成功等等。

func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) float64

这段代码来自一个用于抓取和导出 MySQL 服务的监控指标的 Prometheus Exporter。它的功能主要是连接到数据库,然后使用一组“scraper”(抓取器)收集各种指标,并将这些指标发送到 Prometheus。

以下是逐行解释:

func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) float64 {

该函数定义了一个方法 scrape,属于结构体 Exporter。这个方法接受上下文对象 (context.Context) 和一个只能发送数据的 Prometheus 指标通道 (chan<- prometheus.Metric)。这个方法会返回一个浮点数,表示抓取的成功率。

var err error
scrapeTime := time.Now()
db, err := sql.Open("mysql", e.dsn)

开始抓取时记录当前时间,并尝试打开一个到 MySQL 的数据库连接。e.dsn 是数据库连接信息。

if err != nil {
	level.Error(e.logger).Log("msg", "Error opening connection to database", "err", err)
	return 0.0
}

如果打开数据库连接失败,记录错误日志并返回抓取失败。

defer db.Close()

不管 scrape 方法执行完毕后是否成功或失败,都确保关闭数据库连接。

db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
db.SetConnMaxLifetime(1 * time.Minute)

设置数据库连接的参数:最大开放连接数为 1,最大空闲连接数为 1,连接的最大生存时间为一分钟。

if err := db.PingContext(ctx); err != nil {
	level.Error(e.logger).Log("msg", "Error pinging mysqld", "err", err)
	return 0.0
}

尝试 Ping 数据库来检查连接是否可用。如果 Ping 失败,记录错误并返回抓取失败。

ch <- prometheus.MustNewConstMetric(mysqlScrapeDurationSeconds, prometheus.GaugeValue, time.Since(scrapeTime).Seconds(), "connection")

创建一个新的 Prometheus 指标,代表与 MySQL 连接的时长,并将这个指标发送到指标通道。

version := getMySQLVersion(db, e.logger)
var wg sync.WaitGroup
defer wg.Wait()
for _, scraper := range e.scrapers {

获取 MySQL 的版本信息,然后开始遍历所有的 scraper。

	if version < scraper.Version() {
		continue
	}

如果 MySQL 的版本低于 scraper 所需的版本,跳过这个 scraper。

	wg.Add(1)
	go func(scraper Scraper) {

增加 WaitGroup 的计数器,并在新的 goroutine 中运行 scraper。

		defer wg.Done()
		label := "collect." + scraper.Name()
		scrapeTime := time.Now()

当 scraper 完成时,调用 wg.Done() 减少 WaitGroup 的计数器。同时记录 scraper 开始的时间。

		collectorSuccess := 1.0
		if err := scraper.Scrape(ctx, db, ch, log.With(e.logger, "scraper", scraper.Name())); err != nil {
			level.Error(e.logger).Log("msg", "Error from scraper", "scraper", scraper.Name(), "target", e.getTargetFromDsn(), "err", err)
			collectorSuccess = 0.0
		}

尝试运行 scraper,如果失败则记录错误日志,并将 collectorSuccess 设置为 0 表示抓取失败。

		ch <- prometheus.MustNewConstMetric(mysqlScrapeCollectorSuccess, prometheus.GaugeValue, collectorSuccess, label)
		ch <- prometheus.MustNewConstMetric(mysqlScrapeDurationSeconds, prometheus.GaugeValue, time.Since(scrapeTime).Seconds(), label)
	}(scraper)
}

创建两个新的 Prometheus 指标:一个代表 scraper 的成功率,另一个代表 scraper 运行的时长。然后将它们都发送到指标通道。

return 1.0

这个 scrape 方法最后返回 1.0,表示抓取已经完成。

总结来说,这段代码的目标是连接到 MySQL 数据库,然后使用一批定义在 Exporter 中的 scrappers 来收集不同的监控指标。每个 scraper 负责一部分特定的指标收集任务,这些收集任务会并发执行。所有收集的指标都会被发送到一个传入的 Prometheus 指标通道中。

理解mysql_exporter是如何采集mysql_up指标的。

在Prometheus的MySQL exporter中,mysql_up 是一个内置的指标。它主要用来检测和显示当前MySQL服务是否可用。值为1表示MySQL服务正常运行,0表示MySQL服务不可用。

关于这部分代码,通常你会看到类似以下的代码段:

var (
  up = prometheus.NewDesc(
    prometheus.BuildFQName(namespace, "", "up"),
    "Was the last scrape of MySQL successful.",
    nil, nil,
  )
)

》扩展 @ 1 prometheus.NewDesc
prometheus.NewDesc 是 Prometheus Go client library 中的一个函数,用于创建一个新的 Desc 实例。Desc 是描述性指标的元数据,包括其名称、帮助字符串、标签等。这个对象主要在自定义 collector 中使用。

下面是它的基本用法:

desc := prometheus.NewDesc(
    "my_metric_name",
    "This is the help string for my metric.",
    []string{"label1", "label2"},
    nil,
)

参数说明:

  • "my_metric_name": 这是你的指标的名称。
  • "This is the help string for my metric.": 这是关于你的指标的帮助文本,当有人查看你的指标时,他们能通过这段文字了解这个指标是做什么的。
  • []string{"label1", "label2"}: 这是你的指标的标签的列表。标签是对指标的附加信息,比如http请求的方法(GET,POST)可以作为http请求延迟指标的一个标签。
  • nil: 这是一些常量标签,即无论指标的值如何变化,这些标签都保持不变。例如,如果你的应用运行在多台服务器上,你可能想添加一个常量标签来标记每个指标是从哪个服务器上报告的。

这个函数会返回一个 *prometheus.Desc 对象,你可以在你的 Collector 的 Describe 方法中发送这个 Desc,然后在 Collect 方法中使用它来创建你的指标。

》扩展 @ 2 prometheus.BuildFQName
prometheus.BuildFQName 是 Prometheus 客户端库的一个函数,用于构建全限定名(Fully Qualified Name)。它主要用于定义 metric 的名称。该函数接受三个参数:namespace、subsystem 和 name。

参数解释:

  • namespace: 一个标志性的前缀,通常包含应用或服务的名称。
  • subsystem: 标识系统内部的特定组件,比如 http_server 或 rpc_server 等。
  • name: 指定具体的度量指标的名称,比如 requests_total 或 errors_total 等。

这三个参数会按顺序拼接在一起,中间用下划线(“_”)分隔,形成一个唯一的指标名称。

使用样例:

以下是一个使用示例,在 Golang 中使用 prometheus.BuildFQName 函数来创建一个新的 metric 名称:

package main

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

func main() {
	namespace := "myApp"
	subsystem := "httpServer"
	name := "requestsTotal"

	fqName := prometheus.BuildFQName(namespace, subsystem, name)
	fmt.Println(fqName)
}

运行上述代码后,将会输出:

myApp_httpServer_requestsTotal

这就是通过 prometheus.BuildFQName 构建的全限定指标名称。该名称可以唯一标识你的应用或者服务中的某个具体度量指标,方便进行监控和查询。

此后,在数据抓取函数中,mysql_up被设置成1或0:

func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
  e.mutex.Lock() // To protect metrics from concurrent collects.
  defer e.mutex.Unlock()
  if err := e.scrape(ch); err != nil {
    log.Println("Error scraping for collect:", err)
    ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 0)
    return
  }

  ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 1)
}

以上代码的意思是:如果数据抓取(scrape)过程出现错误,那么将mysql_up设为0,否则设为1。

请注意,这只是一种可能实现方式,并且取决于特定的MySQL exporter实现。具体的代码可能会有所不同。你可以在相应的GitHub仓库上查看详细的代码。

在github里面找到mysql_exporter采集mysql_up指标的代码,并解释每一行代码的意义

对于这个请求,首先我们需要明确一点:mysql_up指标是由MySQL Exporter采集得到的,MySQL Exporter是Prometheus的一个插件,用于从MySQL服务器中获取和暴露各种度量。

MySQL Exporter通常是用Go语言编写的。这意味着你可以在其GitHub存储库中找到源码。然而,该源码可能会包含大量文件和代码行,涵盖了许多不同的指标和功能。

理想情况下,具体实现mysql_up指标的代码可能会分布在多个文件中,并且可能涉及到更复杂的逻辑。这比直接解释一小部分代码要困难得多。

下面我提供一个简化示例,展示如何使用Go导出一个名为mysql_up的指标:

package collector

import (
  "database/sql"
  "github.com/prometheus/client_golang/prometheus"
)

var (
  upDesc = prometheus.NewDesc(
    "mysql_up",
    "Whether MySQL is up.",
    nil, nil,
  )
)

type ScrapeHealthCheck struct{}

func (ScrapeHealthCheck) Name() string {
  return "health_check"
}

func (ScrapeHealthCheck) Help() string {
  return "Check if MySQL is up."
}

func (s ScrapeHealthCheck) Scrape(db *sql.DB, ch chan<- prometheus.Metric) error {
  var dummy string
  err := db.QueryRow("SELECT VERSION();").Scan(&dummy)
  if err != nil {
    ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, 0)
    return nil
  }
  
  ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, 1)
  return nil
}

这段代码的每行都在完成以下任务:

  1. 导入必要的包;
  2. 定义一个全局变量upDesc,用于描述’mysql_up’指标;
  3. 定义一个名为ScrapeHealthCheck的结构体,没有任何成员;
  4. 为这个结构体实现Name()方法,返回采集器的名称;
  5. 实现Help()方法,提供有关此收集器的描述;
  6. 实现Scrape()方法:
    • 该方法接受一个数据库连接和一个channel参数,用于发送Metric值;
    • 执行一个简单的SQL查询以检查MySQL服务器是否在线;
    • 如果执行失败(即MySQL服务器离线),则向channel发送一个Metric,值为0;
    • 如果执行成功(即MySQL服务器在线),则向channel发送一个Metric,值为1。

请注意,这只是一个基础示例,并未涵盖错误处理或更复杂的行为。真正的MySQL Exporter会对各种可能的问题做出更详细的响应。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐