Golang 公共变量包——expvar

写在前面

expvar包是 Golang 官方提供的公共变量包,它可以辅助调试全局变量。支持一些常见的类型:float64int64MapString。如果我们的程序要用到上面提的四种类型(其中,Map 类型要求 Key 是字符串)。可以考虑使用这个包。

功能

  1. 它支持对变量的基本操作,修改、查询这些;

  2. 整形类型,可以用来做计数器;

  3. 操作都是线程安全的。这点很不错。相信大家都自己整过全局变量,除了变量还得整的锁,自己写确实挺麻烦的;

  4. 此外还提供了调试接口,/debug/vars。它能够展示所有通过这个包创建的变量;

  5. 所有的变量都是Var类型,可以自己通过实现这个接口扩展其它的类型;

     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
     }
    
  6. Handler()方法可以得到调试接口的http.Handler,和自己的路由对接。

这些基础的功能就不多说了,大家可以直接看官方的文档

调试接口

看源码的时候发现一个非常有意思的调试接口,/debug/vars会把所有注册的变量打印到接口里面。这个接口很有情怀。

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

源码

var (
    mutex   sync.RWMutex
    vars    = make(map[string]Var)
    varKeys []string // sorted
)
  1. varKeys是全局变量所有的变量名,而且是有序的;

  2. vars根据变量名保存了对应的数据。当然mutex就是这个 Map 的锁;

  3. 这三个变量组合起来其实是一个有序线程安全哈希表的实现。

     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)
     }
    
  4. 这个包里面的所有类型都实现了这个接口;

  5. 以 Int 类型举例。实现非常的简单,注意AddSet方法是线程安全的。别的类型实现也一样

     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
     }
    
  6. 将变量注册到一开始介绍的varsvarKeys里面;

  7. 注册时候也是线程安全的,所有的变量名在注册的最后排了个序;

  8. 创建对象的时候会自动注册。

     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)
     }
    
  9. Do方法,利用一个闭包,按照varKeys的顺序遍历所有全局变量;

  10. expvarHandler方法是http.Handler类型,将所有变量通过接口输出,里面通过Do方法,把所有变量遍历了一遍。挺巧妙;

  11. 通过http.HandleFunc方法把expvarHandler这个外部不可访问的方法对外,这个方法用于对接自己的路由;

  12. 输出数据的类型,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
     }
    
  13. 这是一个非常有意思的写法,它可以把任何类型转换成Var类型;

  14. Func定义的是函数,它的类型是func() interface{}

  15. Func(cmdline),使用的地方需要看清楚,参数是cmdline而不是cmdline(),所以这个写法是类型转换。转换完之后cmdline方法就有了String()方法,在String()方法里又调用了f(),通过 JSON 编码输出。这个小技巧在前面提到的http.HandleFunc里面也有用到,Golang 的程序员对这个是真爱,咱们编码的时候也要多用用啊。

不足

感觉这个包还是针对简单变量,比如整形、字符串这种比较好用。

  1. 前面已经说了,Map 类型只支持 Key 是字符串的变量。其它类型还得自己扩展,扩展的话锁的问题还是得自己搞。而且 JSON 编码低版本不支持 Key 是整形类型的编码,也是个问题;
  2. Var接口太简单,只有一个String()方法,基本上只能输出变量所有内容,别的东西都没办法控制,如果你的变量有10000个键值对,那么这个接口基本上属于不能用。多说一句,这是 Golang 设计的常见问题,比如日志包,输出的类型是io.Writer,而这个接口只支持一个方法Write([]byte),想扩展日志包的功能很难,这也失去了抽象出来一个接口的意义。
  3. 路由里面还默认追加了启动参数和MemStats内存相关参数。我个人觉得后面这个不应该加,调用runtime.ReadMemStats(stats)会引起 Stop The World,总感觉不值当。

总结

看到就写了,并没有什么沉淀,写得挺乱的。这个包很简单,但是里面还是有些可以借鉴的编码和设计。新版本的 Golang 已经能解析整形为 Key 的哈希表了,这个包啥时候能跟上支持一下?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,638评论 18 139
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,830评论 0 6
  • 能力模型 选择题 [primary] 下面属于关键字的是()A. funcB. defC. structD. cl...
    _张晓龙_阅读 24,826评论 14 224
  • 械密者:非典型医疗器械营销案例 孙易之揉了揉鼻梁,深吸一口气,试图让心情稍稍平息,然后盯着那条期待许久的公告链接,...
    械密者阅读 228评论 0 3
  • 我这边一切都很好,只是少了你,这句话,所有异地恋的都会有感触吧。但是很抱歉,今天这篇文章我们不是讲异地恋。而是讲渣...
    420_c644阅读 264评论 0 0