首頁  >  文章  >  後端開發  >  使用 expvar 暴露 Go 程式運行指標

使用 expvar 暴露 Go 程式運行指標

Go语言进阶学习
Go语言进阶学习轉載
2023-07-21 09:52:28668瀏覽
取得應用程式的運行指標,可以讓我們更了解它的實際狀況。將這些指標對接到 prometheus、zabbix 等監控系統,能夠對應用程式持續偵測,發現異常可以及時警告並處理。

Pull 與Push

#與監控系統對接方式有兩種,一種是Pull(拉取) ,另外一種Push(推播)。

以 Prometheus 為例,應用程式透過暴露出 HTTP 接口,讓 Prometheus 週期性地透過該接口抓取指標,這就是 Pull。而 Push 是應用程式主動將指標推送給 PushGateway,而 Prometheus 則去 PushGateway 抓取資料。

Go 標準庫中有一個名為 expvar 的包,它的名字由 exp 和 var 兩部分組合而成,意味著導出變數。

expvar 為公共變數提供了標準化的接口,並透過 HTTP 以 Json 的格式將這些變數暴露出去,很適合採用 Pull 的方式與監控系統進行對接。

使用expvar 庫

expvar 是標準函式庫,意味著我們並不要額外的依賴,而且它還提供了一些開箱即用的指標。下面我們來學習一下該函式庫的使用。

當引用了 expvar 函式庫(<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>則是實作了Var 介面的expvar.Func 類型變量。

Var 接口,它只定義了一個 String 方法。需要注意的是,該方法必須要傳回一個有效的 Json字串。

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

但是, 在 https://go.dev/ 上統計的公共項目,該庫的 import 數量還不到 1萬。

使用 expvar 暴露 Go 程式運行指標

使用 expvar 暴露 Go 程式運行指標

#相比於其他標準函式庫的import 數量而言,expvar 的存在感太低了。

以上是使用 expvar 暴露 Go 程式運行指標的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:Go语言进阶学习。如有侵權,請聯絡admin@php.cn刪除