zoukankan      html  css  js  c++  java
  • 遇到一个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. 一旦内部失败,则循环永久阻塞...
  • 相关阅读:
    基于OpenStack构建企业私有云(8)Cinder
    基于OpenStack构建企业私有云(6)创建第一台云主机
    基于OpenStack创建企业私有云(7)Horizon
    基于OpenStack构建企业私有云(4)Nova
    python--006--三元运算、列表解析、生成器表达式
    python--006--迭代器协议和for循环工作机制
    python--005--文件操作(b,其他)
    python--005--文件操作(r,w,a)
    python--004--函数(其他内置函数)
    python--004--函数(zip、min、max)
  • 原文地址:https://www.cnblogs.com/zolo/p/14439969.html
Copyright © 2011-2022 走看看