Maison >développement back-end >Golang >Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Go语言进阶学习
Go语言进阶学习avant
2023-07-21 09:52:28811parcourir
Obtenir les métriques d'exécution d'une application nous donne une meilleure idée de son fonctionnement réel. En connectant ces indicateurs à des systèmes de surveillance tels que prometheus et zabbix, les applications peuvent être détectées en continu et les anomalies peuvent être alertées et traitées en temps opportun.

Pull et Push

Il existe deux façons d'interfacer avec le système de surveillance, l'une est Pull et l'autre est Push.

Prenons Prometheus comme exemple. L'application expose l'interface HTTP et permet à Prometheus de capturer périodiquement des indicateurs via l'interface. Dans Push, l'application transmet activement les indicateurs à PushGateway et Prometheus accède à PushGateway pour capturer les données.

Il existe un package appelé expvar dans la bibliothèque standard Go. Son nom est une combinaison de exp et var, ce qui signifie exporter des variables.

expvar fournit une interface standardisée pour les variables publiques et expose ces variables au format Json via HTTP, ce qui est très approprié pour se connecter au système de surveillance à l'aide de Pull.

Utilisation de la bibliothèque expvar

expvar est une bibliothèque standard, ce qui signifie que nous n'avons pas besoin de dépendances supplémentaires, et elle fournit également des indicateurs prêts à l'emploi. Apprenons à utiliser cette bibliothèque.

Lorsque la bibliothèque expvar est référencée (<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">import "expvar"</span>),以下 init 函数将被自动调用。

func init() {
 http.HandleFunc("/debug/vars", expvarHandler)
 Publish("cmdline", Func(cmdline))
 Publish("memstats", Func(memstats))
}

该函数为我们注册了 /debug/vars 路径的 HTTP 服务,访问该路径将得到 Json 格式的指标。

因此,我们还需要调用 ListenAndServe 绑定端口,并开始服务 HTTP 请求。

http.ListenAndServe(":8080", nil)

完整代码如下

package main

import (
 _ "expvar"
 "net/http"
)

func main() {
 http.ListenAndServe(":8080", nil)
}

将程序运行起来后,通过 curl 请求,得到以下结果

$ curl localhost:8080/debug/vars
{
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build1657217338/b001/exe/main"],
"memstats": {"Alloc":278880,"TotalAlloc":278880,"Sys":8735760,"Lookups":0,"Mallocs":1169,"Frees":87,"HeapAlloc":278880,"HeapSys":3866624,"HeapIdle":2949120,"HeapInuse":917504,"HeapReleased":2899968,"HeapObjects":1082,"StackInuse":327680,"StackSys":327680,"MSpanInuse":28696,"MSpanSys":32640,"MCacheInuse":9600,"MCacheSys":15600,"BuckHashSys":3875,"GCSys":3826448,"OtherSys":662893,"NextGC":4194304,"LastGC":0,"PauseTotalNs":0,"PauseNs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":0,"NumForcedGC":0,"GCCPUFraction":0,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":41,"Frees":0},{"Size":16,"Mallocs":496,"Frees":0},{"Size":24,"Mallocs":63,"Frees":0},{"Size":32,"Mallocs":28,"Frees":0},{"Size":48,"Mallocs":134,"Frees":0},{"Size":64,"Mallocs":50,"Frees":0},{"Size":80,"Mallocs":17,"Frees":0},{"Size":96,"Mallocs":17,"Frees":0},{"Size":112,"Mallocs":6,"Frees":0},{"Size":128,"Mallocs":9,"Frees":0},{"Size":144,"Mallocs":9,"Frees":0},{"Size":160,"Mallocs":18,"Frees":0},{"Size":176,"Mallocs":6,"Frees":0},{"Size":192,"Mallocs":0,"Frees":0},{"Size":208,"Mallocs":37,"Frees":0},{"Size":224,"Mallocs":6,"Frees":0},{"Size":240,"Mallocs":0,"Frees":0},{"Size":256,"Mallocs":12,"Frees":0},{"Size":288,"Mallocs":7,"Frees":0},{"Size":320,"Mallocs":2,"Frees":0},{"Size":352,"Mallocs":13,"Frees":0},{"Size":384,"Mallocs":1,"Frees":0},{"Size":416,"Mallocs":30,"Frees":0},{"Size":448,"Mallocs":1,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":0,"Frees":0},{"Size":576,"Mallocs":5,"Frees":0},{"Size":640,"Mallocs":5,"Frees":0},{"Size":704,"Mallocs":3,"Frees":0},{"Size":768,"Mallocs":0,"Frees":0},{"Size":896,"Mallocs":6,"Frees":0},{"Size":1024,"Mallocs":8,"Frees":0},{"Size":1152,"Mallocs":10,"Frees":0},{"Size":1280,"Mallocs":3,"Frees":0},{"Size":1408,"Mallocs":1,"Frees":0},{"Size":1536,"Mallocs":0,"Frees":0},{"Size":1792,"Mallocs":7,"Frees":0},{"Size":2048,"Mallocs":2,"Frees":0},{"Size":2304,"Mallocs":3,"Frees":0},{"Size":2688,"Mallocs":2,"Frees":0},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3200,"Mallocs":0,"Frees":0},{"Size":3456,"Mallocs":0,"Frees":0},{"Size":4096,"Mallocs":8,"Frees":0},{"Size":4864,"Mallocs":1,"Frees":0},{"Size":5376,"Mallocs":1,"Frees":0},{"Size":6144,"Mallocs":2,"Frees":0},{"Size":6528,"Mallocs":0,"Frees":0},{"Size":6784,"Mallocs":0,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":2,"Frees":0},{"Size":9472,"Mallocs":8,"Frees":0},{"Size":9728,"Mallocs":0,"Frees":0},{"Size":10240,"Mallocs":0,"Frees":0},{"Size":10880,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":0,"Frees":0},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14336,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":18432,"Mallocs":0,"Frees":0}]}
}

可以看到,expvar 默认已提供了两项指标,分别是程序执行命令(os.Args)和运行时内存分配(runtime.Memstats)信息。

expvar 库重点内容

expvar 中最重要的是 Publish 函数和 Var 接口。

func Publish(name string, v Var) {}

Publish 函数签名中需要两个参数,name 是我们指定的指标名。例如在上文 expvar 的 init 函数下 <span style="font-size: 15px;">Publish("cmdline", Func(cmdline))</span>代码行,其中<span style="font-size: 15px;">cmdline</span>就是指标名,而<span style="font-size: 15px;">Func(cmdline)</span>import "expvar"), la fonction init suivante sera appelée automatiquement.

// Var is an abstract type for all exported variables.
type Var interface {
 // String returns a valid JSON value for the variable.
 // Types with String methods that do not return valid JSON
 // (such as time.Time) must not be used as a Var.
 String() string
}
Cette fonction enregistre pour nous le service HTTP du chemin /debug/vars. L'accès à ce chemin obtiendra des indicateurs au format Json.

🎜🎜🎜 Par conséquent, nous devons également appeler ListenAndServe pour lier le port et commencer à traiter les requêtes HTTP. 🎜🎜
type Int struct {
 i int64
}

type Float struct {
 f uint64
}

type Map struct {
 m      sync.Map // map[string]Var
 keysMu sync.RWMutex
 keys   []string // sorted
}

type String struct {
 s atomic.Value // string
}

type Func func() any
🎜🎜Le code complet est le suivant🎜🎜
intVar = expvar.NewInt(“metricName”)
floatVar = expvar.NewFloat(“metricName”)
mapVar = expvar.NewMap(“metricName”)
stringVar = expvar.NewString(“metricName”)
🎜🎜Après avoir exécuté le programme, via la requête curl, les résultats suivants sont obtenus🎜🎜
func NewInt(name string) *Int {
 v := new(Int)
 Publish(name, v)
 return v
}
🎜🎜Comme vous pouvez le voir, expvar a fourni deux indicateurs par défaut, qui sont commandes d'exécution du programme (os.Args) et informations sur l'allocation de mémoire d'exécution (runtime.Memstats). 🎜🎜

Contenu clé de la bibliothèque expvar🎜

🎜🎜Les éléments les plus importants dans expvar sont la fonction Publier et l'interface Var. 🎜🎜
type MyStruct struct {
 Field1 string
 Field2 int
 Field3 float64
}
🎜🎜La signature de la fonction de publication nécessite deux paramètres, name est le nom de l'indicateur que nous avons spécifié. Par exemple, sous la fonction init de expvar ci-dessus 🎜🎜Publish("cmdline", Func(cmdline))🎜🎜ligne de code, où🎜🎜cmdline🎜🎜 est le nom de l'indicateur, et 🎜🎜Func(cmdline)🎜🎜 est expvar.Func qui implémente le Variable de type d'interface Var. 🎜🎜🎜🎜Interface Var, qui définit uniquement une méthode String. Il convient de noter que cette méthode doit renvoyer une chaîne Json valide. 🎜🎜
// Var is an abstract type for all exported variables.
type Var interface {
 // String returns a valid JSON value for the variable.
 // Types with String methods that do not return valid JSON
 // (such as time.Time) must not be used as a Var.
 String() string
}

为了方便使用,expvar 库中提供了五种导出变量类型,它们均实现了 Var 接口。

type Int struct {
 i int64
}

type Float struct {
 f uint64
}

type Map struct {
 m      sync.Map // map[string]Var
 keysMu sync.RWMutex
 keys   []string // sorted
}

type String struct {
 s atomic.Value // string
}

type Func func() any

我们分别通过调用 expvar.NewXXX 函数即可完成前四种类型的变量创建与指标注册。

intVar = expvar.NewInt(“metricName”)
floatVar = expvar.NewFloat(“metricName”)
mapVar = expvar.NewMap(“metricName”)
stringVar = expvar.NewString(“metricName”)

例如 expvar.NewInt 函数,它会内部调用 Publish 方法完成指标名与 expvar.Int 类型变量的绑定。

func NewInt(name string) *Int {
 v := new(Int)
 Publish(name, v)
 return v
}

而 expvar.Func 类型,其实是为了让我们可以自定义导出类型。

例如,假如我们想要暴露以下定义的结构体

type MyStruct struct {
 Field1 string
 Field2 int
 Field3 float64
}

首先需要创建一个数据生成函数。它用于在每次调用 HTTP 服务路径时,通过该函数导出这里面的数据。

func MyStructData() interface{} {
 return MyStruct{
  Field1: "Gopher",
  Field2: 22,
  Field3: 19.99,
 }
}

最后,通过 Publish 函数注册指标名即可。

expvar.Publish("metricName", expvar.Func(MyStructData))

完整示例

下面,我们给出一个覆盖五种导出变量类型的完整示例。

package main

import (
 "expvar"
 "github.com/shirou/gopsutil/v3/host"
 "github.com/shirou/gopsutil/v3/load"
 "github.com/shirou/gopsutil/v3/mem"
 "net/http"
 "time"
)

type Load struct {
 Load1  float64
 Load5  float64
 Load15 float64
}

func AllLoadAvg() interface{} {
 return Load{
  Load1:  LoadAvg(1),
  Load5:  LoadAvg(5),
  Load15: LoadAvg(15),
 }
}

func LoadAvg(loadNumber int) float64 {
 avg, _ := load.Avg()
 switch loadNumber {
 case 5:
  return avg.Load5
 case 15:
  return avg.Load15
 default:
  return avg.Load1
 }
}

func main() {
 var (
  aliveOfSeconds = expvar.NewInt("aliveOfSeconds")
  hostID         = expvar.NewString("hostID")
  lastLoad       = expvar.NewFloat("lastLoad")
  virtualMemory  = expvar.NewMap("virtualMemory")
 )
 expvar.Publish("allLoadAvg", expvar.Func(AllLoadAvg))
 h, _ := host.HostID()
 hostID.Set(h)

 go http.ListenAndServe(":8080", nil)

 for {
  aliveOfSeconds.Add(1)
  vm, _ := mem.VirtualMemory()
  lastLoad.Set(LoadAvg(1))
  virtualMemory.Add("active", int64(vm.Active))
  virtualMemory.Add("buffer", int64(vm.Buffers))
  time.Sleep(1 * time.Second)
 }
}

在上述示例中,我们通过 gopsutil 库(介绍可见还在自己写 Go 系统监控函数吗一文)获取了一些系统信息,并展示了如何通过 expvar 中的各种变量类型将这些信息进行导出。

curl 访问 localhost:8080/debug/vars 结果如下

$ curl localhost:8080/debug/vars
{
"aliveOfSeconds": 1,
"allLoadAvg": {"Load1":1.69580078125,"Load5":1.97412109375,"Load15":1.90283203125},
"cmdline": ["/var/folders/xk/gn46n46d503dsztbc6_9qb2h0000gn/T/go-build3566019824/b001/exe/main"],
"hostID": "7a1a74f2-30fc-5bc1-b439-6b7aef22e58d",
"lastLoad": 1.69580078125,
"memstats": {"Alloc":256208,"TotalAlloc":256208,"Sys":8735760,"Lookups":0,"Mallocs":1089,"Frees":48,"HeapAlloc":256208,"HeapSys":3866624,"HeapIdle":2891776,"HeapInuse":974848,"HeapReleased":2859008,"HeapObjects":1041,"StackInuse":327680,"StackSys":327680,"MSpanInuse":19992,"MSpanSys":32640,"MCacheInuse":9600,"MCacheSys":15600,"BuckHashSys":3905,"GCSys":3851120,"OtherSys":638191,"NextGC":4194304,"LastGC":0,"PauseTotalNs":0,"PauseNs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"PauseEnd":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"NumGC":0,"NumForcedGC":0,"GCCPUFraction":0,"EnableGC":true,"DebugGC":false,"BySize":[{"Size":0,"Mallocs":0,"Frees":0},{"Size":8,"Mallocs":35,"Frees":0},{"Size":16,"Mallocs":415,"Frees":0},{"Size":24,"Mallocs":71,"Frees":0},{"Size":32,"Mallocs":37,"Frees":0},{"Size":48,"Mallocs":141,"Frees":0},{"Size":64,"Mallocs":52,"Frees":0},{"Size":80,"Mallocs":20,"Frees":0},{"Size":96,"Mallocs":23,"Frees":0},{"Size":112,"Mallocs":14,"Frees":0},{"Size":128,"Mallocs":7,"Frees":0},{"Size":144,"Mallocs":7,"Frees":0},{"Size":160,"Mallocs":18,"Frees":0},{"Size":176,"Mallocs":6,"Frees":0},{"Size":192,"Mallocs":1,"Frees":0},{"Size":208,"Mallocs":42,"Frees":0},{"Size":224,"Mallocs":3,"Frees":0},{"Size":240,"Mallocs":0,"Frees":0},{"Size":256,"Mallocs":9,"Frees":0},{"Size":288,"Mallocs":8,"Frees":0},{"Size":320,"Mallocs":5,"Frees":0},{"Size":352,"Mallocs":13,"Frees":0},{"Size":384,"Mallocs":3,"Frees":0},{"Size":416,"Mallocs":33,"Frees":0},{"Size":448,"Mallocs":0,"Frees":0},{"Size":480,"Mallocs":2,"Frees":0},{"Size":512,"Mallocs":1,"Frees":0},{"Size":576,"Mallocs":4,"Frees":0},{"Size":640,"Mallocs":8,"Frees":0},{"Size":704,"Mallocs":3,"Frees":0},{"Size":768,"Mallocs":1,"Frees":0},{"Size":896,"Mallocs":6,"Frees":0},{"Size":1024,"Mallocs":8,"Frees":0},{"Size":1152,"Mallocs":9,"Frees":0},{"Size":1280,"Mallocs":3,"Frees":0},{"Size":1408,"Mallocs":1,"Frees":0},{"Size":1536,"Mallocs":1,"Frees":0},{"Size":1792,"Mallocs":9,"Frees":0},{"Size":2048,"Mallocs":1,"Frees":0},{"Size":2304,"Mallocs":2,"Frees":0},{"Size":2688,"Mallocs":2,"Frees":0},{"Size":3072,"Mallocs":0,"Frees":0},{"Size":3200,"Mallocs":1,"Frees":0},{"Size":3456,"Mallocs":0,"Frees":0},{"Size":4096,"Mallocs":5,"Frees":0},{"Size":4864,"Mallocs":0,"Frees":0},{"Size":5376,"Mallocs":1,"Frees":0},{"Size":6144,"Mallocs":1,"Frees":0},{"Size":6528,"Mallocs":0,"Frees":0},{"Size":6784,"Mallocs":0,"Frees":0},{"Size":6912,"Mallocs":0,"Frees":0},{"Size":8192,"Mallocs":1,"Frees":0},{"Size":9472,"Mallocs":8,"Frees":0},{"Size":9728,"Mallocs":0,"Frees":0},{"Size":10240,"Mallocs":0,"Frees":0},{"Size":10880,"Mallocs":0,"Frees":0},{"Size":12288,"Mallocs":0,"Frees":0},{"Size":13568,"Mallocs":0,"Frees":0},{"Size":14336,"Mallocs":0,"Frees":0},{"Size":16384,"Mallocs":0,"Frees":0},{"Size":18432,"Mallocs":0,"Frees":0}]},
"virtualMemory": {"active": 1957449728, "buffer": 0}
}

总结

标准库 expvar 为需要导出的公共变量提供了一个标准化的接口,使用比较简单。

expvar 包内部定义的几种基础类型都相应给出了并发安全的操作方法,我们不需要去重复实现一遍,能够开箱即用。

Cependant, selon les statistiques des projets publics sur https://go.dev/, le nombre d'importations de cette bibliothèque est inférieur à 10 000.

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Utilisez expvar pour exposer les métriques en cours d'exécution du programme Go

Par rapport au nombre d'importations dans d'autres bibliothèques standards, la présence d'expvar est trop faible.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer