pacakge expvar 内部实现

内部数据结构

// 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
}

// All published variables.
var (
	mutex   sync.RWMutex
	vars    = make(map[string]Var)
	varKeys []string // sorted
)

vars 的 key 是 Var 绑定的名称,Var 是 一个有 String() 方法的 interface。

func NewInt(name string) *Int {
	v := new(Int)
	Publish(name, v)
	return v
}

// Publish declares a named exported variable. This should be called from a
// package's init function when it creates its Vars. If the name is already
// registered then this will log.Panic.
func Publish(name string, v Var) {
	mutex.Lock()
	defer mutex.Unlock()
	if _, existing := vars[name]; existing {
		log.Panicln("Reuse of exported var name:", name)
	}
	vars[name] = v
	varKeys = append(varKeys, name)
	sort.Strings(varKeys)
}

NewInt 创建一个名字为 name 的 Int 对象,并将这个对象名字和对应对象的指针保存到 map vars, 并返回这个对象的指针 Int对象的指针有 Add()、Set()、String()、Value()等方法

// Int is a 64-bit integer variable that satisfies the Var interface.
type Int struct {
	i int64
}

func (v *Int) Value() int64 {
	return atomic.LoadInt64(&v.i)
}

func (v *Int) String() string {
	return strconv.FormatInt(atomic.LoadInt64(&v.i), 10)
}

func (v *Int) Add(delta int64) {
	atomic.AddInt64(&v.i, delta)
}

func (v *Int) Set(value int64) {
	atomic.StoreInt64(&v.i, value)
}

expvar 会向 http.DefaultServeMux 注册了 /debug/vars 的 handler 如果你的 Server 有自己的 handler ,那么 需要手动注册一下 /debug/var 的 handler

func init() {
	http.HandleFunc("/debug/vars", expvarHandler)
	Publish("cmdline", Func(cmdline))
	Publish("memstats", Func(memstats))
}

使用示例1: 不使用自己的hander

package  main

import (
 "expvar"
 "net/http"
 "fmt"
)

var visits = expvar.NewInt("visits")

func handleFunc (w http.ResponseWriter, r *http.Request) {
	visits.Add(1)
	fmt.Fprintf(w,"你是第%d 访客\n", visits.Value())
}

func main()  {
	http.HandleFunc("/", handleFunc)
	http.ListenAndServe(":8080", nil)
}

使用实例2: 使用自己的 handler

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "net/http"
    "log"
    "expvar"
)

var visited = expvar.NewInt("visited")

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	visited.Add(1)
    fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	visited.Add(1)
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func Vars(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	expvar.Handler().ServeHTTP(w, r)
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)
    router.GET("/debug/vars", Vars)

    log.Fatal(http.ListenAndServe(":8080", router))
}