前言
你是否曾經(jīng)遇到過這樣的情況,在開發(fā)環(huán)境排查問題,因為一些數(shù)據(jù)保存在了一些全局變量中,這些變量往往是一個 map 或者是一個數(shù)組,想看看在運行過程中,這里面究竟存放了什么數(shù)據(jù),有時不得不在運行的時候?qū)⑺敵龅饺罩局校敲慈绻蚁雽崟r看到這些數(shù)據(jù)的情況又怎么辦呢?
其實 golang 中已經(jīng)存在這樣的庫,就是來做這個事情的 expvar
使用案例
廢話不多數(shù),直接上案例
package main
import (
"expvar"
"net/http"
)
var (
s map[string]string
user User
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func showMap() interface{} {
return s
}
func showUser() interface{} {
return user
}
func main() {
user.Name = "tom"
user.Age = 18
s = make(map[string]string, 10)
s["1"] = "111"
s["2"] = "222"
expvar.Publish("a_map", expvar.Func(showMap))
expvar.Publish("b_user", expvar.Func(showUser))
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
s["3"] = "333"
})
http.ListenAndServe(":8080", nil)
}
監(jiān)控變量
直接訪問 http://127.0.0.1:8080/debug/vars 你就能看到一個 json 格式的返回數(shù)據(jù),數(shù)據(jù)如下所示:
{
"a_map": {
"1": "111",
"2": "222"
},
"b_user": {
"name": "tom",
"age": 18
},
"cmdline": [
"/private/var/folders/37/qpz6_ndd1w72bhrg2sgg042r0000gn/T/___go_build_go_demo_vars"
],
"memstats": {
"Alloc": 188992,
"TotalAlloc": 188992,
"Sys": 70453248,
"Lookups": 0,
"Mallocs": 818,
"Frees": 21,
"HeapAlloc": 188992,
"HeapSys": 66650112,
"HeapIdle": 65716224,
"HeapInuse": 933888,
"HeapReleased": 65683456,
"HeapObjects": 797,
"StackInuse": 458752,
"StackSys": 458752,
"MSpanInuse": 15776,
"MSpanSys": 16384,
"MCacheInuse": 6944,
"MCacheSys": 16384,
"BuckHashSys": 2638,
"GCSys": 2240512,
"OtherSys": 1068466,
"NextGC": 4473924,
"LastGC": 0,
"PauseTotalNs": 0,
......
}
}
這里面一方面將你的在代碼中全局變量顯示了出來還有個 cmdline 和 memstats 這個我們后面再說
這是你可以訪問一次 http://127.0.0.1:8080/test 然后再回來看看,就會發(fā)現(xiàn)變量的值以及改變,說明這個變量顯示的是實時的
監(jiān)控原理
其實原理非常簡單,你自己都能寫,下面就是源碼中的一部分
// Handler returns the expvar HTTP Handler.
//
// This is only needed to install the handler in a non-standard location.
func Handler() http.Handler {
return http.HandlerFunc(expvarHandler)
}
func cmdline() interface{} {
return os.Args
}
func memstats() interface{} {
stats := new(runtime.MemStats)
runtime.ReadMemStats(stats)
return *stats
}
func init() {
http.HandleFunc("/debug/vars", expvarHandler)
Publish("cmdline", Func(cmdline))
Publish("memstats", Func(memstats))
}
其實就是在 init 的時候注冊了對應(yīng)的路由,還注冊了 cmdline 和 memstats 兩個值,這兩個值很有用:
- cmdline 展示了當(dāng)前啟動時通過命令行傳遞的參數(shù)是什么
- memstats 展示了當(dāng)前運行時內(nèi)存的使用情況,還有 gc 的部分信息等等
其他方法
https://golang.org/pkg/expvar/
當(dāng)然這個包不止有 Publish 方法,還有 Add、Set 等等,個人最常用的還是 Publish
監(jiān)控內(nèi)存并展示
既然 expvar 暴露了內(nèi)存的使用情況,那我們當(dāng)然能利用這個信息來作圖了,所以就推薦一個很好用的庫
安裝之后,就可以用命令通過之前的接口來監(jiān)控內(nèi)存等使用情況咯
jplot --url http://127.0.0.1:8080/debug/vars \
memstats.HeapSys+memstats.HeapAlloc+memstats.HeapIdle+marker,counter:memstats.NumGC \
counter:memstats.TotalAlloc \
memstats.HeapObjects \
memstats.StackSys+memstats.StackInuse

我覺得這樣的使用方式在本地和開發(fā)環(huán)境的時候測試更加輕量,不用部署一些監(jiān)控軟件,即開即測;當(dāng)然線上環(huán)境肯定會有 prometheus + grafana 這樣的監(jiān)控神器,這里也只是拋個磚頭。
總結(jié)
如果線上需要監(jiān)控一些全局變量的使用情況可以考慮使用 expvar 進(jìn)行監(jiān)控和查看,或者用它來監(jiān)控你的配置文件或者一些任務(wù)數(shù)量等等也是一個不錯的選擇。