官术网_书友最值得收藏!

48.1 expvar包的工作原理

Go標準庫中的expvar包提供了一種輸出應用內部狀態信息的標準化方案,這個方案標準化了以下三方面內容:

  • 數據輸出接口形式;
  • 輸出數據的編碼格式;
  • 用戶自定義性能指標的方法。

Go應用通過expvar包輸出內部狀態信息的工作原理如圖48-1所示。

015-01

圖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所示的結果。

015-01

圖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
}
主站蜘蛛池模板: 海安县| 区。| 南靖县| 龙井市| 黔西县| 伊吾县| 肇州县| 两当县| 定南县| 卢氏县| 黄浦区| 德钦县| 木兰县| 屏边| 蓝山县| 荃湾区| 开原市| 皋兰县| 丘北县| 泸定县| 时尚| 扶余县| 陕西省| 古蔺县| 淅川县| 祥云县| 闸北区| 广灵县| 广东省| 孟津县| 花莲县| 宁强县| 宜兴市| 肥乡县| 慈利县| 海原县| 平利县| 明水县| 呼和浩特市| 元氏县| 皮山县|