• 遇到一个golang time.Tick的坑


    time.Tick的巨坑

    go版本

    >go version
    go version go1.15.4 windows/amd64
    

    api文档

    func Tick(d Duration) <-chan Time
        Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. 
        While Tick is useful for clients that have no need to shut down the Ticker, be aware that 
        without a way to shut it down the underlying Ticker cannot be recovered by the garbage 
        collector; it "leaks". Unlike NewTicker, Tick will return nil if d <= 0.
    

    问题源码

    • 现象: etcd关闭expired后循环不再进入
    	for range time.Tick(time.Duration(ticker) * time.Second) {
    		now := time.Now().Unix()
    		lvs := make(map[string]*LeaseResult)
    		leaseIndex.Range(func(key, value interface{}) bool {
    			if v := value.(*LeaseResult); v.Exp <= now {
    				lvs[key.(string)] = v
    			}
    			return true
    		})
    		var err error
    		for lk, lv := range lvs {
    			if err = client.SendKeepAlive(lv.Id); err != nil {
    				if LeaseTimeoutError(err) {
    					// 如果stream关闭(etcd连接断开)重连后相关的lease可能过期,届时会报错
    					if DEBUG {
    						base.DefaultLogger.Debugf("etcd lease reset (send): lid=%x, key=%v", lv.Id, lk)
    					}
    					if id, ttl, err := client.Grant(expire); err == nil {
    						if err = client.Put(lk, lv.Val, id); err == nil {
    							lv.Id = id
    							lv.Exp = Exp(now, ttl)
    							leaseIndex.Store(lk, lv)
    						}
    					}
    				} else {
    					return err
    				}
    			}
    			if DEBUG {
    				base.DefaultLogger.Debugf("etcd lease send: lid=%x, key=%v, err=%v", lv.Id, lk, err)
    			}
    		}
    	}
    

    解决源码

        // FIXBUG: 必须注意, 此处不能使用"for range ticker"的写法,在etcd关闭expire+后循环不再执行! 相当奇怪!
    	for {
    		now := time.Now().Unix()
    		lvs := make(map[string]*LeaseResult)
    		leaseIndex.Range(func(key, value interface{}) bool {
    			if v := value.(*LeaseResult); v.Exp <= now {
    				lvs[key.(string)] = v
    			}
    			return true
    		})
    		var err error
    		for lk, lv := range lvs {
    			if err = client.SendKeepAlive(lv.Id); err != nil {
    				if LeaseTimeoutError(err) {
    					// 如果stream关闭(etcd连接断开)重连后相关的lease可能过期,届时会报错
    					if DEBUG {
    						base.DefaultLogger.Debugf("etcd lease reset (send): lid=%x, key=%v", lv.Id, lk)
    					}
    					if id, ttl, err := client.Grant(expire); err == nil {
    						if err = client.Put(lk, lv.Val, id); err == nil {
    							lv.Id = id
    							lv.Exp = Exp(now, ttl)
    							leaseIndex.Store(lk, lv)
    						}
    					}
    				} else {
    					return err
    				}
    			}
    			if DEBUG {
    				base.DefaultLogger.Debugf("etcd lease send: lid=%x, key=%v, err=%v", lv.Id, lk, err)
    			}
    		}
    		time.Sleep(time.Duration(ticker) * time.Second)
    	}
    

    原因归结

    1. time.Tick()生成某个channel
    2. 内部往channel push
    3. 循环从channel pop
    4. 一旦内部失败,则循环永久阻塞...
  • 相关阅读:
    树形目录生成器.bat
    Google 搜索截图
    Expo 2010 Japan Pavilion
    WinCE应用程序开发创建文件或文件夹
    Oracle 获取每月最后一天的函数
    Oracle的外连接符号(+)
    关于项目中找不到某个配置xml文件的问题
    通過反編譯跟蹤JSP頁面
    JSP 一些基本语法
    page request session application 范围
  • 原文地址:https://www.cnblogs.com/zolo/p/14439969.html
Copyright © 2020-2023  润新知