



Go标准库中的expvar包提供了一种输出应用内部状态信息的 标准化方案 ,这个方案标准化了以下三方面内容:
Go应用通过expvar包输出内部状态信息的工作原理如图48-1所示。
图48-1 expvar包工作原理
从图48-1中我们看到,Go应用如果需要输出自身状态数据,需要以下面的形式导入expvar:
import _ "expvar"
和net/http/pprof类似,expvar包也在自己的init函数中向http包的默认请求“路由器”DefaultServeMux注册一个服务端点/debug/vars:
// $GOROOT/src/expvar/expvar.go
func init() {
http.HandleFunc("/debug/vars", expvarHandler)
...
}
这个服务端点就是expvar提供给外部的获取应用内部状态的 唯一标准接口 ,外部工具(无论是命令行还是基于Web的图形化程序)都可以通过标准的http get请求从该服务端点获取应用内部状态数据。下面是一个简单的例子:
// chapter8/sources/expvar_demo1.go
package main
import (
_ "expvar"
"fmt"
"net/http"
)
func main() {
http.Handle("/hi", http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
w.Write([]byte("hi"))
}))
fmt.Println(http.ListenAndServe("localhost:8080", nil))
}
运行上述示例后,通过浏览器访问http://localhost:8080/debug/vars将得到如图48-2所示的结果。
图48-2 通过浏览器访问expvar包注册的服务端点
如果应用程序本身并没有使用默认“路由器”DefaultServeMux,那么我们需要手动将expvar包的服务端点注册到应用程序所使用的“路由器”上。expvar包提供了Handler函数,该函数可用于其内部expvarHandler的注册。
// expvar_demo2.go
package main
import (
"expvar"
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.Handle("/hi", http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
w.Write([]byte("hi"))
}))
mux.Handle("/debug/vars", expvar.Handler())
fmt.Println(http.ListenAndServe("localhost:8080", mux))
}
如果应用程序本身并没有启动HTTP服务,那么还需在一个单独的goroutine中启动一个HTTP服务,这样expvar提供的服务才能有效。
从图48-2中我们还可以看到,expvar包提供的内部状态服务端点返回的是 标准 的JSON格式数据。样例如下:
{
"cmdline": ["/var/folders/cz/sbj5kg2d3m3c6j650z0qfm800000gn/T/go-build507091832/ b001/exe/expvar_demo2"],
"memstats": {
"Alloc": 223808,
"TotalAlloc": 223808,
"Sys": 71387144,
"Lookups": 0,
"Mallocs": 743,
"Frees": 11,
...
}
}
在默认返回的状态数据中包含了两个字段:cmdline和memstats。这两个输出数据是expvar包在init函数中就已经发布(Publish)了的变量:
//$GOROOT/src/expvar/expvar.go
func init() {
http.HandleFunc("/debug/vars", expvarHandler)
Publish("cmdline", Func(cmdline))
Publish("memstats", Func(memstats))
}
cmdline字段的含义是输出数据的应用名,这里因为是通过go run运行的应用,所以cmdline的值是一个临时路径下的应用。
而memstats输出的数据对应的是runtime.Memstats结构体,反映的是应用在运行期间堆内存分配、栈内存分配及GC的状态。runtime.Memstats结构体的字段可能会随着Go版本的演进而发生变化,其字段具体含义可以参考Memstats结构体中的注释。
//$GOROOT/src/expvar/expvar.go
func memstats() interface{} {
stats := new(runtime.MemStats)
runtime.ReadMemStats(stats)
return *stats
}