1

本文主要研究一下gorm的prometheus

Plugin

gorm.io/gorm@v1.20.10/interfaces.go

// Plugin GORM plugin interface
type Plugin interface {
    Name() string
    Initialize(*DB) error
}
Plugin接口定义了Name、Initialize方法

Prometheus

gorm.io/plugin/prometheus@v0.0.0-20201023060415-b0e68fc269af/prometheus.go

type Prometheus struct {
    *gorm.DB
    *DBStats
    *Config
    refreshOnce, pushOnce sync.Once
    Labels                map[string]string
    collectors            []prometheus.Collector
}

func (p *Prometheus) Name() string {
    return "gorm:prometheus"
}

func (p *Prometheus) Initialize(db *gorm.DB) error { //can be called repeatedly
    p.DB = db

    if p.Config.DBName != "" {
        p.Labels["db_name"] = p.Config.DBName
    }

    p.DBStats = newStats(p.Labels)

    p.refreshOnce.Do(func() {
        for _, mc := range p.MetricsCollector {
            p.collectors = append(p.collectors, mc.Metrics(p)...)
        }

        go func() {
            for range time.Tick(time.Duration(p.Config.RefreshInterval) * time.Second) {
                p.refresh()
            }
        }()
    })

    if p.Config.StartServer {
        go p.startServer()
    }

    if p.PushAddr != "" {
        go p.startPush()
    }

    return nil
}
Prometheus实现了Plugin接口定义了Name、Initialize方法;Initialize方法这里执行了p.startServer()、p.startPush()以及定时任务执行p.refresh()

startServer

gorm.io/plugin/prometheus@v0.0.0-20201023060415-b0e68fc269af/prometheus.go

func (p *Prometheus) startServer() {
    httpServerOnce.Do(func() { //only start once
        mux := http.NewServeMux()
        mux.Handle("/metrics", promhttp.Handler())
        err := http.ListenAndServe(fmt.Sprintf(":%d", p.Config.HTTPServerPort), mux)
        if err != nil {
            p.DB.Logger.Error(context.Background(), "gorm:prometheus listen and serve err: ", err)
        }
    })
}
startServer启动了http server提供了/metrics接口

startPush

gorm.io/plugin/prometheus@v0.0.0-20201023060415-b0e68fc269af/prometheus.go

func (p *Prometheus) startPush() {
    p.pushOnce.Do(func() {
        pusher := push.New(p.PushAddr, p.DBName)

        for _, collector := range p.DBStats.Collectors() {
            pusher = pusher.Collector(collector)
        }

        for _, c := range p.collectors {
            pusher = pusher.Collector(c)
        }

        for range time.Tick(time.Duration(p.Config.RefreshInterval) * time.Second) {
            err := pusher.Push()
            if err != nil {
                p.DB.Logger.Error(context.Background(), "gorm:prometheus push err: ", err)
            }
        }
    })
}
startPush方法启动定时任务去执行pusher.Push()

refresh

gorm.io/plugin/prometheus@v0.0.0-20201023060415-b0e68fc269af/prometheus.go

func (p *Prometheus) refresh() {
    if db, err := p.DB.DB(); err == nil {
        p.DBStats.Set(db.Stats())
    } else {
        p.DB.Logger.Error(context.Background(), "gorm:prometheus failed to collect db status, got error: %v", err)
    }
}
refresh方法主要是更新p.DBStats

Use

gorm.io/gorm@v1.20.10/gorm.go

func (db *DB) Use(plugin Plugin) (err error) {
    name := plugin.Name()
    if _, ok := db.Plugins[name]; !ok {
        if err = plugin.Initialize(db); err == nil {
            db.Plugins[name] = plugin
        }
    } else {
        return ErrRegistered
    }

    return err
}
gorm的Use方法用于启动一个plugin,它会执行plugin.Initialize(db)

实例

    db.Use(prometheus.New(prometheus.Config{
        DBName:          "db1", // 使用 `DBName` 作为指标 label
        RefreshInterval: 15,    // 指标刷新频率(默认为 15 秒)
        PushAddr:        "prometheus pusher address", // 如果配置了 `PushAddr`,则推送指标
        StartServer:     true,  // 启用一个 http 服务来暴露指标
        HTTPServerPort:  8080,  // 配置 http 服务监听端口,默认端口为 8080 (如果您配置了多个,只有第一个 `HTTPServerPort` 会被使用)
        MetricsCollector: []prometheus.MetricsCollector {
            &prometheus.MySQL{
                VariableNames: []string{"Threads_running"},
            },
        },  // 用户自定义指标
    }))

小结

gorm的Plugin接口定义了Name、Initialize方法;gorm的Use方法用于启动一个plugin,它会执行plugin.Initialize(db);Prometheus实现了Plugin接口定义了Name、Initialize方法;Initialize方法这里执行了p.startServer()、p.startPush()以及定时任务执行p.refresh()。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论