varKeys是全局变量所有的变量名,而且是有序的;vars根据变量名保存了对应的数据。当然mutex就是这个 Map 的锁;-
这三个变量组合起来其实是一个有序线程安全哈希表的实现。
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 } 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) } - 这个包里面的所有类型都实现了这个接口;
-
以 Int 类型举例。实现非常的简单,注意
Add和Set方法是线程安全的。别的类型实现也一样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) } func NewInt(name string) *Int { v := new(Int) Publish(name, v) return v } - 将变量注册到一开始介绍的
vars和varKeys里面; - 注册时候也是线程安全的,所有的变量名在注册的最后排了个序;
-
创建对象的时候会自动注册。
func Do(f func(KeyValue)) { mutex.RLock() defer mutex.RUnlock() for _, k := range varKeys { f(KeyValue{k, vars[k]}) } } func expvarHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") fmt.Fprintf(w, "{\n") first := true Do(func(kv KeyValue) { if !first { fmt.Fprintf(w, ",\n") } first = false fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) }) fmt.Fprintf(w, "\n}\n") } func Handler() http.Handler { return http.HandlerFunc(expvarHandler) } Do方法,利用一个闭包,按照varKeys的顺序遍历所有全局变量;expvarHandler方法是http.Handler类型,将所有变量通过接口输出,里面通过Do方法,把所有变量遍历了一遍。挺巧妙;- 通过
http.HandleFunc方法把expvarHandler这个外部不可访问的方法对外,这个方法用于对接自己的路由; -
输出数据的类型,
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value),可以发现,值输出的字符串,所以输出的内容是String()的结果。这里有一个技巧,虽然调用的字符串的方法,但是由于输出格式%s外面并没有引号,所有对于 JSON 来说,输出的内容是对象类型。相当于在 JSON 编码的时候做了一次类型转换。type Func func() interface{} func (f Func) Value() interface{} { return f() } func (f Func) String() string { v, _ := json.Marshal(f()) return string(v) } func cmdline() interface{} { return os.Args } - 这是一个非常有意思的写法,它可以把任何类型转换成
Var类型; Func定义的是函数,它的类型是func() interface{}Func(cmdline),使用的地方需要看清楚,参数是cmdline而不是cmdline(),所以这个写法是类型转换。转换完之后cmdline方法就有了String()方法,在String()方法里又调用了f(),通过 JSON 编码输出。这个小技巧在前面提到的http.HandleFunc里面也有用到,Golang 的程序员对这个是真爱,咱们编码的时候也要多用用啊。
Golang 公共变量包——expvar
原文链接:
blog.cyeam.com