1 数据采集原理
这里我通过 mysql 监控开发,来演示其实监控其他的第三方程序都是一样的
这里有一个 exporter 暴露一个 /metrics 的 api , 但是这又有一个第三方应用程序,exporter 如何去获取到当前第三方程序的采集数据,因为他获取数据以后才能够实现暴露。
exporter 对外提供接口,这个接口并不是一个 web 的 api ,比如我们的 mysql 协议就是一种 API ,但是有的 exporter 会提供 http 接口或者接口写到文件里面去,如 Linux 的内存使用信息或者 cpu 使用信息我们可以在 /proc 里面获取
获取文件信息
如下读取文件获取信息:
# 获取内存信息
[16:52:14 root@go ~]#cat /proc/meminfo
MemTotal: 3861300 kB
MemFree: 2257728 kB
MemAvailable: 2897096 kB
....
# 获取 cpu 信息
[18:21:20 root@go ~]#cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 23
model : 96
model name : AMD Ryzen 7 4800H with Radeon Graphics
stepping : 1
microcode : 0x8600104
...
获取程序信息
如果是监控 mysql 的话我们可以通过执行 sql 语句获取信息
[18:25:08 root@go ~]#mysql -u root -p123456
# 获取数据库的运行状态
MariaDB [(none)]> show global status;
# 获取数据库链接信息
MariaDB [(none)]> show global variables;
获取命令信息
我们也可以通过命令来获取当前信息
[18:32:43 root@go ~]#free -h
total used free shared buff/cache available
Mem: 3.7G 693M 2.1G 11M 879M 2.8G
Swap: 6.0G 0B 6.0G
以上就是获取数据的几种方式,当我知道这些数据之后我们就可以定时的来进行调用,如果是采集的时间比较长那就不适合用在监控中触发
2 MySQL 监控资源采集项
对 mysql 的监控资源采集一般我们关系的是下面两个数据资源,这两条命令查询出来的数据是 KEY:V 格式,但是需要将那些数据暴露给 Prometheus 呢
# 获取数据库的运行状态
MariaDB [(none)]> show global status;
# 获取数据库链接信息
MariaDB [(none)]> show global variables;
# 开启慢查询
MariaDB [(none)]> set global slow_query_log = true;
一般常用监控资源如下:
mysq1 可用性
操作失败
select 1; # 如果在mysql 中执行 sql 语句失败也是不可用状态
ping # 如果ping mysql 失败也是一个不可用状态
慢查询次数,也就是对于执行的 sql 语句速度比较慢的进行统计,慢查询次数是影响数据库性能的
show global status where variable_name='slow_queries';
容量: 当前执行 sql 语句的总数
show global status where variable_name='Queries';
操作:想知道不同的操作如: insert, update, delete 操作的数量
show global status where Variable_name like 'com_insert';
show global status where Variable_name like 'com_update';
show global status where Variable_name like 'com_delete';
show global status where Variable_name like 'com_select';
show global status where Variable_name like 'com_replace';
连接:
# 当前链接数
show global status where variable_name='Threads_connected';
# 当前最大链接数。后期可以配置告警,当我们的连接数量和我的最大连接数如果差值为 20 的时候产生报警
show global variables where variable_name='max_connections';
流量:
# 发送流量
show global status where variable_name='Bytes_ received';
# 接收流量
show global status where variable_name= 'Bytes_sent';
我们拿到了 mysql 的监控资源采集指标就可以通过这些资源进行暴露
2.1 开发 mysql 监控指标
现在我们已经知道了要采集那些 mysql 的指标,那指标如何采集到,我们就需要定下来采集指标的类型以及我们采集指标的触发时间
数据库监控项采集类型定义:
/*
A.带固定标签的指标项
1.MySQL 存活状态,定义类型:gauage
2.mysql 监控的地址信息暴露,定义类型:gauage
B.指标项:
1.慢查询数量,定义类型:counter,不用带 label
2.数据库执行操作数量,定义类型:counter,不用带 label
3.数据库执行查询数量,定义类型:counter,不用带 label
这里只统计 insert, update, delete select 语句的数量,带可变label
4.统计流量,定义类型:counter,不用带 label
5.统计连接数量,定义类型:gauage,不用带 label
C.指标类型和触发时间:
在采集 API 的时候进行触发
*/
2.1.1 MySQL存活状态指标采集范例
package main
import (
"database/sql"
"log"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 连接数据库
dsn := "root:123456@tcp(10.0.0.3:3306)/mysql"
mySqlAddr := "localhost:3306"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
// 定义指标
// 1.检查 mysql 是否存活,是通过 gauage 指标类型定义,
// 2.prometheus.MustRegister 注册 mysql_up 指标
prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Namespace: "mysql",
Name: "mysql_up",
Help: "Mysql UP info",
ConstLabels: prometheus.Labels{"addr": mySqlAddr},
}, func() float64 {
// 采集数据通过 db.ping 来对数据库进行存活检查,如果 err 为 nil 表示连接成功返回 1 否则返回 0
if err := db.Ping(); err == nil {
return 1
} else {
return 0
}
}))
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
浏览器访问,可以看到状态码为 1 ,已经链接mysql,mysql 存活检查成功
验证程序,这里我将10.0.0.3 服务器上的 mysql stop 不掉
[22:28:27 root@go exec]#systemctl stop mariadb.service
浏览器访问状态码为 0 状态失败,所以我们这个程序已经能够实现对数据库的存活状态监控
我这里再将 mariadb 数据库启动
[00:04:44 root@go ~]#systemctl start mariadb.service
状态已经为 1 ,后期就通过这种方式来对 mysql 的状态进行检测
2.1.2 慢查询指标范例
我通过定义 collector 接口来实现对数据库的慢查询采集,并且单独写到 collectors 目录中
1.编写链接数据库程序
package linkmysql
import (
"database/sql"
"log"
)
func LinkDB() *sql.DB {
// 链接 mysql
dsn := "root:123456@tcp(10.0.0.3:3306)/mysql"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
return db
}
2.编写慢查询监控项
package collectors
import (
"database/sql"
"log"
"github.com/prometheus/client_golang/prometheus"
)
type SlowQueriesCollector struct {
// 定义 db 属性类型为 *sql.DB 结构体
db *sql.DB
slowDesc *prometheus.Desc
}
// 通过调用结构体属性来查询监控指标
func (c *SlowQueriesCollector) Describe(descs chan<- *prometheus.Desc) {
// 将指标描述信息写入 descs 只写管道中
descs <- c.slowDesc
}
// 对监控指标采集数据
func (c *SlowQueriesCollector) Collect(metric chan<- prometheus.Metric) {
// 因为采集慢查询有两个数据值,所以需要定义两个变量
var (
name string
count float64
)
// 执行单条 sql 查询语句,并将返回值重新赋值给 name, count
if err := c.db.QueryRow("show global status where variable_name='slow_queries';").Scan(&name, &count); err != nil {
log.Panic(err)
}
metric <- prometheus.MustNewConstMetric(
c.slowDesc, // 采集监控指标
prometheus.CounterValue, // 指标类型为 counter 对递增、递减的指标进行采集
count, // 采集指标的值
)
}
// 注册监控指标项
func SlowQueriesRegister(db *sql.DB) {
prometheus.MustRegister(&SlowQueriesCollector{
// 赋值 db
db: db,
slowDesc: prometheus.NewDesc(
// 指标名称
"Mysql_globel_status_slow_queries",
// 指标帮助
"MySQL 慢查询指标",
// 没有可变标签
nil,
// 没有固定标签
nil,
),
})
}
3.编写 mysql 存活指标项
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
func MysqlUp(db *sql.DB) {
// 定义标签
mySqlAddr := "localhost:3306"
// 1.检查 mysql 是否存活,是通过 gauage 指标类型定义,
// 2.prometheus.MustRegister 注册 mysql_up 指标
prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Namespace: "mysql",
Name: "mysql_up",
Help: "Mysql 存活检查",
ConstLabels: prometheus.Labels{"addr": mySqlAddr},
}, func() float64 {
// 采集数据通过 db.ping 来对数据库进行存活检查,如果 err 为 nil 表示连接成功返回 1 否则返回 0
if err := db.Ping(); err == nil {
return 1
} else {
return 0
}
}))
}
4.到 main 程序调用 mysql 存活和 慢查询
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 链接数据库
db := linkmysql.LinkDB()
// 调用 MysqlUp 监控指标
collectors.MysqlUp(db)
// 调用慢查询
collectors.SlowQueriesRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
5.启动程序浏览器访问
6.验证慢查询,这里我查询一条 sql 语句并通过 sleep 函数人为产生一条查询
MariaDB [mysql]> select sleep(12), Host from user where Host like 'l%' ;
+-----------+-----------+
| sleep(12) | Host |
+-----------+-----------+
| 0 | localhost |
| 0 | localhost |
+-----------+-----------+
2 rows in set (24.01 sec) # 因为有两条数据所以耗时 24 秒
7.此时我们可以看到在慢查询指标中就触发了一条记录
2.1.3 采集当前执行 sql 语句总数范例
这里我对 qps 语句进行查询,由于 qps 也是属于在查询语句中的,所以和慢查询语句放到同一个文件中
1.编写采集指标
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
// 定义 Qps 语句的监控指标
type QpsCollector struct {
db *sql.DB
desc *prometheus.Desc
}
// 查询监控指标
func (c *QpsCollector) Describe(descs chan<- *prometheus.Desc) {
descs <- c.desc
}
// 采集监控指标
func (c *QpsCollector) Collect(metric chan<- prometheus.Metric) {
var (
name string
count float64
)
// 获取监控指标
if err := c.db.QueryRow("show global status where variable_name='Queries';").Scan(&name, &count); err != nil {
log.Panic(err)
}
metric <- prometheus.MustNewConstMetric(
c.desc, // 采集监控指标
prometheus.CounterValue, // 采集数据类型
count, // 采集指标的值
)
}
// 注册并给 QpsCollector 结构体赋值
func QpsRegister(db *sql.DB) {
prometheus.MustRegister(&QpsCollector{
db: db,
desc: prometheus.NewDesc(
"MySQL_QPS", // 采集名称
"MySQL 当前执行语句总数采集", // 采集帮助信息
nil, // 可变标签
nil, // 固定标签
),
})
}
2.连接数据库
package linkmysql
import (
"database/sql"
"log"
)
func LinkDB() *sql.DB {
// 链接 mysql
dsn := "root:123456@tcp(10.0.0.3:3306)/mysql"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
return db
}
3.main 程序调用
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
webAddr := ":9090"
// 调用 qps 语句监控指标
collectors.QpsRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
3.浏览器访问
4.mysql 中执行一条sql 语句用来验证
MariaDB [(none)]> show global status where variable_name='Queries';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Queries | 397 | # 397 条数据查询
+---------------+-------+
5.浏览器验证
之所以 398 因为程序在执行本身他就会去执行 show global status where variable_name='Queries';
SQL 语句
2.1.4 采集当前 mysql 操作语句范例
我这里单独创建一个 comd.go 程序,用来采集数据库的执行语句数据
并且 mysql 中的增删改查语句都是带有可变标签的,我们将 增删改查 这四个动作写到同一个指标里面,通过标签来进行区分
1.编写采集指标
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type CommandCollector struct {
db *sql.DB
desc *prometheus.Desc
}
// 定义采集监控指标
func (c *CommandCollector) Describe(desc chan<- *prometheus.Desc) {
desc <- c.desc
}
// 采集监控指标
func (c *CommandCollector) Collect(metric chan<- prometheus.Metric) {
var (
name string
count float64
)
// 执行 sql 语句获取 insert 数量
c.db.QueryRow("show global status where variable_name like 'com_insert';").Scan(&name, &count)
// 采集 insert 语句指标
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, count, "insert")
// 执行 sql 语句获取 delete 数量
c.db.QueryRow("show global status where variable_name like 'com_delete';").Scan(&name, &count)
// 采集 delete 语句指标
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, count, "delete")
// 执行 sql 语句获取 update 数量
c.db.QueryRow("show global status where variable_name like 'com_update';").Scan(&name, &count)
// 采集 update 语句指标
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, count, "update")
// 执行 sql 语句获取 select 数量
c.db.QueryRow("show global status where variable_name like 'com_select';").Scan(&name, &count)
// 采集 select 语句指标
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, count, "select")
}
// 注册
func SqlRegister(db *sql.DB) {
prometheus.MustRegister(&CommandCollector{
db: db,
desc: prometheus.NewDesc(
"mysql_sql", // 指标名称
"mysql_sql 指标获取", // 指标帮助
[]string{"command"}, // 指标可变标签
nil, // 由于是可变标签没有固定标签
),
})
}
2.连接数据库
package linkmysql
import (
"database/sql"
"log"
)
func LinkDB() *sql.DB {
// 链接 mysql
dsn := "root:123456@tcp(10.0.0.3:3306)/mysql"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
return db
}
3.main 程序调用
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 链接数据库
db := linkmysql.LinkDB()
// 调用 sql 语句监控指标
collectors.SqlRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
4.浏览器访问
2.1.5 修改代码去掉冗余性
但是我们发现在采集指标项的时候有太多的代码都是相同的,所以我们为了让他不要这么冗余、我们可以对代码进行修改,这里单独编写一个 mysqlCollector 结构体,由于是该程序内部调用,所以我们这里首字母为小写
1.编写 mysql 查询语句
package collectors
import "database/sql"
type mysqlCollector struct {
db *sql.DB
}
// 通过 sql 语句来获取 status 监控项
func (c *mysqlCollector) status(name string) float64 {
var (
newname string
total float64
)
sql := "show global status where variable_name=?"
c.db.QueryRow(sql, name).Scan(&newname, &total)
return total
}
// 通过 sql 语句来获取 variable 监控项
func (c *mysqlCollector) variable(name string) float64 {
var (
newname string
total float64
)
sql := "show global variables where variable_name=?"
c.db.QueryRow(sql, name).Scan(&newname, &total)
return total
}
2.修改 queries 程序代码
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type SlowQueriesCollector struct {
mysqlCollector // 匿名结构体组合 mysqlCollector
slowDesc *prometheus.Desc
}
// 通过调用结构体属性来查询监控指标
func (c *SlowQueriesCollector) Describe(desc chan<- *prometheus.Desc) {
// 将指标描述信息写入 descs 只写管道中
desc <- c.slowDesc
}
// 对慢查询指标采集数据
func (c *SlowQueriesCollector) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(
c.slowDesc, // 采集监控指标
prometheus.CounterValue, // 指标类型为 counter 对递增、递减的指标进行采集
c.sql("slow_queries"), // 采集慢查询指标的值
)
}
// 注册慢查询监控项
func SlowQueriesRegister(db *sql.DB) {
prometheus.MustRegister(&SlowQueriesCollector{
// 赋值 db 到 mysqlCollector 结构体组合中,拿到当前数据库信息
mysqlCollector: mysqlCollector{db},
slowDesc: prometheus.NewDesc(
"Mysql_globel_status_slow_queries", // 指标名称
"MySQL 慢查询指标", // 指标帮助
nil, // 没有可变标签
nil, // 没有固定标签
),
})
}
// 定义 Qps 语句的监控指标
type QpsCollector struct {
mysqlCollector // 匿名结构体组合 mysqlCollector
desc *prometheus.Desc
}
// 查询监控指标
func (c *QpsCollector) Describe(descs chan<- *prometheus.Desc) {
descs <- c.desc
}
// 采集当前执行 sql 语句监控指标
func (c *QpsCollector) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(
c.desc, // 采集监控指标
prometheus.CounterValue, // 采集数据类型
c.sql("Queries"), // 采集当前执行 sql 语句指标的值
)
}
// 注册并给 QpsCollector 结构体赋值
func QpsRegister(db *sql.DB) {
prometheus.MustRegister(&QpsCollector{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"MySQL_QPS", // 采集名称
"MySQL 当前执行语句总数采集", // 采集帮助信息
nil, // 可变标签
nil, // 固定标签
),
})
}
3.修改 sql 查询程序代码
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type CommandCollector struct {
mysqlCollector
desc *prometheus.Desc
}
// 定义采集监控指标
func (c *CommandCollector) Describe(desc chan<- *prometheus.Desc) {
desc <- c.desc
}
// 采集监控指标
func (c *CommandCollector) Collect(metric chan<- prometheus.Metric) {
// 采集 insert,delete,update,select 语句
sqlCmd := []string{"com_insert", "com_delete", "com_update", "com_select"}
for _, sql := range sqlCmd {
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, c.sql(sql), sql)
}
}
// 注册
func SqlRegister(db *sql.DB) {
prometheus.MustRegister(&CommandCollector{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"mysql_sql", // 指标名称
"mysql_sql 指标获取", // 指标帮助
[]string{"command"}, // 指标可变标签
nil, // 由于是可变标签没有固定标签
),
})
}
启动程序,浏览器访问实现监控指标获取
2.1.6 mysql链接数指标范例
我们修改了代码的冗余之后,接下来就编写 mysql 链接指标范例,编写一个 connection.go 的程序
1.编写链接数据
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type ConnectionCollector struct {
mysqlCollector
// 由于有两个指标所以这里我写到一起
connectedDesc *prometheus.Desc
maxconnectedDesc *prometheus.Desc
}
// 每个收集器都必须实现descripe函数
func (c *ConnectionCollector) Describe(desc chan<- *prometheus.Desc) {
desc <- c.connectedDesc
desc <- c.maxconnectedDesc
}
// 采集监控指标
func (c *ConnectionCollector) Collect(metric chan<- prometheus.Metric) {
// 当前链接数指标
metric <- prometheus.MustNewConstMetric(
c.connectedDesc, // 采集监控指标
prometheus.CounterValue, // 采集值类型
c.status("Threads_connected"), // 采集 “已连接的线程” 监控指标
)
// 最大链接数指标
metric <- prometheus.MustNewConstMetric(
c.maxconnectedDesc,
prometheus.CounterValue,
c.variable("max_connections"),
)
}
// 注册
func ConnectionRegister(db *sql.DB) {
prometheus.MustRegister(&ConnectionCollector{
mysqlCollector: mysqlCollector{db},
connectedDesc: prometheus.NewDesc(
"mysql_connection", // 指标名称
"mysql 当前链接采集", // 指标帮助信息
nil, // 没有可变标签
nil, // 没有固定标签
),
maxconnectedDesc: prometheus.NewDesc(
"mysql_MAX_connection",
"mysql 最大链接数",
nil,
nil,
),
})
}
2.在 main 程序中注册
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 链接数据库
db := linkmysql.LinkDB()
// 调用连接数监控项
collectors.ConnectionRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
浏览器访问
2.1.7 mysql 流量指标获取范例
我们都知道流量是可变的所以这里我采用 Gauge 类型进行检测
1.编写 Flow 程序
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type Flow struct {
mysqlCollector
desc *prometheus.Desc // 因为他们都属一同一类指标所以用一个指标项
}
// 获取收集器
func (c *Flow) Describe(desc chan<- *prometheus.Desc) {
desc <- c.desc
}
// 采集监控指标
func (c *Flow) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, c.status("Bytes_ received"), "发送流量")
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, c.status("Bytes_sent"), "接收流量")
}
// 注册
func FlowRegister(db *sql.DB) {
prometheus.MustRegister(&Flow{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"mysql_Folw",
"mysql 流量监控",
[]string{"flow_to"},
nil,
),
})
}
2.在 main 程序中注册
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 链接数据库
db := linkmysql.LinkDB()
// 调用流量监测监控项
collectors.FlowRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}
浏览器访问
3 代码总览
以上就是一个 exporter 基本的开发流程:
-
数据收集
-
数据指标监控
-
注册数据
所有的第三方 exporter 都是这样的一个流程:
-
我们要知道数据暴露的这么一个接口
-
需要采集哪些数据进行暴露
项目地址:https://github.com/As9530272755/Prometheus_exporter/tree/main/mysql_exporter
代码流程:
项目目录结构:
1.编写链接数据库代码 linkmysql.go
,如果后期我们采集其他数据就连接其他服务
package linkmysql
import (
"database/sql"
"log"
)
func LinkDB() *sql.DB {
// 链接 mysql
dsn := "root:123456@tcp(10.0.0.3:3306)/mysql"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
return db
}
2.编写数据库执行 sql 语句程序mysql.go
package collectors
import "database/sql"
type mysqlCollector struct {
db *sql.DB
}
// 通过 sql 语句来获取 status 监控项
func (c *mysqlCollector) status(name string) float64 {
var (
newname string
total float64
)
sql := "show global status where variable_name=?"
c.db.QueryRow(sql, name).Scan(&newname, &total)
return total
}
// 通过 sql 语句来获取 variable 监控项
func (c *mysqlCollector) variable(name string) float64 {
var (
newname string
total float64
)
sql := "show global variables where variable_name=?"
c.db.QueryRow(sql, name).Scan(&newname, &total)
return total
}
3.编写数据库存活状态监控项代码,mysqlup.go
程序
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
func MysqlUp(db *sql.DB) {
// 定义标签
mySqlAddr := "localhost:3306"
// 1.检查 mysql 是否存活,是通过 gauage 指标类型定义,
// 2.prometheus.MustRegister 注册 mysql_up 指标
prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{
Namespace: "mysql",
Name: "mysql_up",
Help: "Mysql 存活检查",
ConstLabels: prometheus.Labels{"addr": mySqlAddr},
}, func() float64 {
// 采集数据通过 db.ping 来对数据库进行存活检查,如果 err 为 nil 表示连接成功返回 1 否则返回 0
if err := db.Ping(); err == nil {
return 1
} else {
return 0
}
}))
}
4.慢查询程序和当前执行的 sql 语句程序放到了一个程序文件中,queries.go
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type SlowQueriesCollector struct {
mysqlCollector // 匿名结构体组合 mysqlCollector
slowDesc *prometheus.Desc
}
// 通过调用结构体属性来查询监控指标
func (c *SlowQueriesCollector) Describe(desc chan<- *prometheus.Desc) {
// 将指标描述信息写入 descs 只写管道中
desc <- c.slowDesc
}
// 对慢查询指标采集数据
func (c *SlowQueriesCollector) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(
c.slowDesc, // 采集监控指标
prometheus.CounterValue, // 指标类型为 counter 对递增、递减的指标进行采集
c.status("slow_queries"), // 采集慢查询指标的值
)
}
// 注册慢查询监控项
func SlowQueriesRegister(db *sql.DB) {
prometheus.MustRegister(&SlowQueriesCollector{
// 赋值 db 到 mysqlCollector 结构体组合中,拿到当前数据库信息
mysqlCollector: mysqlCollector{db},
slowDesc: prometheus.NewDesc(
"Mysql_globel_status_slow_queries", // 指标名称
"MySQL 慢查询指标", // 指标帮助
nil, // 没有可变标签
nil, // 没有固定标签
),
})
}
// 定义 Qps 语句的监控指标
type QpsCollector struct {
mysqlCollector // 匿名结构体组合 mysqlCollector
desc *prometheus.Desc
}
// 查询监控指标
func (c *QpsCollector) Describe(descs chan<- *prometheus.Desc) {
descs <- c.desc
}
// 采集当前执行 sql 语句监控指标
func (c *QpsCollector) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(
c.desc, // 采集监控指标
prometheus.CounterValue, // 采集数据类型
c.status("Queries"), // 采集当前执行 sql 语句指标的值
)
}
// 注册并给 QpsCollector 结构体赋值
func QpsRegister(db *sql.DB) {
prometheus.MustRegister(&QpsCollector{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"MySQL_QPS", // 采集名称
"MySQL 当前执行语句总数采集", // 采集帮助信息
nil, // 可变标签
nil, // 固定标签
),
})
}
5.所有的 sql 语句查询,comd.go
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type CommandCollector struct {
mysqlCollector
desc *prometheus.Desc
}
// 定义采集监控指标
func (c *CommandCollector) Describe(desc chan<- *prometheus.Desc) {
desc <- c.desc
}
// 采集监控指标
func (c *CommandCollector) Collect(metric chan<- prometheus.Metric) {
// 采集 insert,delete,update,select 语句
sqlCmd := []string{"com_insert", "com_delete", "com_update", "com_select"}
for _, sql := range sqlCmd {
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.CounterValue, c.status(sql), sql)
}
}
// 注册
func CommandRegister(db *sql.DB) {
prometheus.MustRegister(&CommandCollector{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"mysql_sql", // 指标名称
"sql 语句指标获取", // 指标帮助
[]string{"command"}, // 指标可变标签
nil, // 由于是可变标签没有固定标签
),
})
}
6.数据库链接数监控项程序,connection.go
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type ConnectionCollector struct {
mysqlCollector
// 由于有两个指标所以这里我写到一起
connectedDesc *prometheus.Desc
maxconnectedDesc *prometheus.Desc
}
// 每个收集器都必须实现descripe函数
func (c *ConnectionCollector) Describe(desc chan<- *prometheus.Desc) {
desc <- c.connectedDesc
desc <- c.maxconnectedDesc
}
// 采集监控指标
func (c *ConnectionCollector) Collect(metric chan<- prometheus.Metric) {
// 当前链接数指标
metric <- prometheus.MustNewConstMetric(
c.connectedDesc, // 采集监控指标
prometheus.CounterValue, // 采集值类型
c.status("Threads_connected"), // 采集 “已连接的线程” 监控指标
)
// 最大链接数指标
metric <- prometheus.MustNewConstMetric(
c.maxconnectedDesc,
prometheus.CounterValue,
c.variable("max_connections"),
)
}
// 注册
func ConnectionRegister(db *sql.DB) {
prometheus.MustRegister(&ConnectionCollector{
mysqlCollector: mysqlCollector{db},
connectedDesc: prometheus.NewDesc(
"mysql_connection", // 指标名称
"mysql 当前链接采集", // 指标帮助信息
nil, // 没有可变标签
nil, // 没有固定标签
),
maxconnectedDesc: prometheus.NewDesc(
"mysql_MAX_connection",
"mysql 最大链接数",
nil,
nil,
),
})
}
7.编写数据库流量监控项程序,flow.go
package collectors
import (
"database/sql"
"github.com/prometheus/client_golang/prometheus"
)
type Flow struct {
mysqlCollector
desc *prometheus.Desc
}
func (c *Flow) Describe(desc chan<- *prometheus.Desc) {
desc <- c.desc
}
func (c *Flow) Collect(metric chan<- prometheus.Metric) {
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, c.status("Bytes_ received"), "发送流量")
metric <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, c.status("Bytes_sent"), "接收流量")
}
func FlowRegister(db *sql.DB) {
prometheus.MustRegister(&Flow{
mysqlCollector: mysqlCollector{db},
desc: prometheus.NewDesc(
"mysql_Folw",
"mysql 流量监控",
[]string{"flow_to"},
nil,
),
})
}
8.最后在 main 程序中调用所有监控项
package main
import (
"mysqlmonitor/collectors"
"mysqlmonitor/linkmysql"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// MySQL 存活状态指标采集
func main() {
webAddr := ":9090"
// 链接数据库
db := linkmysql.LinkDB()
// 调用 MysqlUp 监控指标
collectors.MysqlUp(db)
// 调用慢查询监控指标
collectors.SlowQueriesRegister(db)
// 调用 qps 语句监控指标
collectors.QpsRegister(db)
// 调用 sql 执行语句监控指标
collectors.CommandRegister(db)
// 调用连接数监控项
collectors.ConnectionRegister(db)
// 调用流量监测监控项
collectors.FlowRegister(db)
// 3.暴露指标
http.Handle("/metrics/", promhttp.Handler())
http.ListenAndServe(webAddr, nil)
}