使用 expvar 暴露 Go 程式運行指標
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萬。
#相比於其他標準函式庫的import 數量而言,expvar 的存在感太低了。
以上是使用 expvar 暴露 Go 程式運行指標的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

在開發中,我們經常會有熱開關的需求,即特定功能在程式運作中的適當時候對它進行開啟或關閉。例如效能分析中所使用的 pprof 取樣,就是一種典型的熱開關。本文將討論如何將這種熱開關做得更酷。

取得應用程式的運作指標,可以讓我們更了解它的實際狀況。將這些指標對接到 prometheus、zabbix 等監控系統,能夠對應用程式持續偵測,發現異常可以及時警告並處理。

Go是一種受歡迎的程式語言,與其他程式語言相比,Go的編譯速度較快,記憶體消耗較少。但是,有時候我們的Go程式會因為缺乏依賴而導致編譯失敗。那麼,為什麼會發生這種情況呢?首先,我們要先了解Go編譯的原理。 Go是靜態編譯型語言,也就是在編譯時就將程式翻譯成機器碼,然後直接執行。與動態編譯型語言相比,Go的編譯過程更為複雜,因為在編譯之前,需要將所有要使用的套件都轉

最近,越來越多的人開始使用GoQUIC來建立網路應用程式。由於其高效的傳輸性能和可靠性,GoQUIC已成為許多專案的首選。但是,在實際使用過程中,一些開發者發現他們的Go程式無法正確使用GoQUIC程式庫。下面,我們就來分析一下可能導致Go程式無法正常使用GoQUIC函式庫的原因。 1.版本問題首先,你要確定你的GoQUIC版本是否是最新的。 GoQUIC經常更新,如果

在公司的不斷發展中,一開始大多是大單體,改造慢了,一個倉庫會有使用十幾年的情況,倉庫的規模基本上是不斷增大的過程。

Go是一種高效率的程式語言,提供了記憶體管理方面的特殊機制。然而,即使在使用這種語言的時候也有可能出現一些問題,例如「outofmemory」錯誤。那麼,為什麼我的Go程式會出現這種錯誤呢?記憶體洩漏記憶體洩漏是一種常見的問題,它在Go語言中同樣存在。當你的Go程式分配了大量的內存,並在執行某些操作後沒有徹底釋放這些內存時,就會出現內存洩漏問題。如果出現記憶體洩漏

Golang(Go)是一門語言,它非常擅長處理錯誤和異常情況。與其他語言不同,Go透過簡單而有效的錯誤處理機制來處理異常情況。儘管Go的錯誤處理機制非常強大且靈活,但某些程式設計師在程式中實作錯誤處理時仍會遇到麻煩。這篇文章旨在幫助解決關於為什麼Go程式中的異常處理無效的問題,以及如何正確地處理異常情況。在Go中無效的異常處理通常是因為程式設計師未正確處理錯誤或誤

在使用Go語言進行開發過程中,難免會遇到各種不同的錯誤。其中一個常見的錯誤是“coredumped”,這個錯誤訊息可能會讓一些開發者感到困惑。本文將說明這個錯誤的原因以及如何解決它。 「coredumped」的意思在Linux作業系統中,「coredumped」是一種錯誤訊息,它表示一個行程在執行過程中意外退出,並且已經產生了一個所謂的「core」檔案。這
