github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgCache/MemoryTtlCache.go (about)

     1  package kmgCache
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/bronze1man/kmg/kmgMath"
     9  	"github.com/golang/groupcache/singleflight"
    10  )
    11  
    12  var CacheMiss = errors.New("cache miss")
    13  
    14  type ttlCacheEntry struct {
    15  	Value   interface{}
    16  	Timeout time.Time
    17  }
    18  
    19  func (entry ttlCacheEntry) GetTtl() uint32 {
    20  	ttlDur := entry.Timeout.Sub(time.Now())
    21  	if ttlDur < 0 {
    22  		ttlDur = 0
    23  	}
    24  	return uint32(kmgMath.CeilToInt(ttlDur.Seconds()))
    25  }
    26  
    27  //请调用 NewTtlCache() 初始化
    28  type TtlCache struct {
    29  	cache       map[string]ttlCacheEntry
    30  	lock        sync.RWMutex
    31  	singleGroup singleflight.Group
    32  }
    33  
    34  func NewTtlCache() *TtlCache {
    35  	return &TtlCache{
    36  		cache: map[string]ttlCacheEntry{},
    37  	}
    38  }
    39  
    40  //如果f 返回的 err不是空,则不会把数据保存在缓存里面,但是会返回另外2项.
    41  func (s *TtlCache) DoWithTtl(key string, f func() (value interface{}, ttl uint32, err error)) (value interface{}, ttl uint32, err error) {
    42  	entry, err := s.get(key)
    43  	if err == nil {
    44  		return entry.Value, entry.GetTtl(), nil
    45  	}
    46  	if err != CacheMiss {
    47  		return
    48  	}
    49  	entryi, err := s.singleGroup.Do(key, func() (interface{}, error) {
    50  		value, ttl, err := f()
    51  		timeout := time.Now().Add(time.Duration(ttl) * time.Second)
    52  		return ttlCacheEntry{
    53  			Value:   value,
    54  			Timeout: timeout,
    55  		}, err
    56  	})
    57  	entry = entryi.(ttlCacheEntry)
    58  	ttl = entry.GetTtl()
    59  	if err == nil && ttl > 0 {
    60  		s.save(key, entry)
    61  	}
    62  	return entry.Value, ttl, nil
    63  }
    64  
    65  func (s *TtlCache) save(key string, entry ttlCacheEntry) {
    66  	s.lock.Lock()
    67  	defer s.lock.Unlock()
    68  	s.cache[key] = entry
    69  	return
    70  }
    71  
    72  func (s *TtlCache) get(key string) (entry ttlCacheEntry, err error) {
    73  	s.lock.RLock()
    74  	defer s.lock.RUnlock()
    75  	now := time.Now()
    76  	entry, ok := s.cache[key]
    77  	if !ok {
    78  		return entry, CacheMiss
    79  	}
    80  	if now.After(entry.Timeout) {
    81  		return entry, CacheMiss
    82  	}
    83  	return entry, nil
    84  }
    85  
    86  //要有个进程在一边进行gc,避免内存泄漏
    87  func (s *TtlCache) GcThread() {
    88  	for {
    89  		time.Sleep(time.Hour)
    90  		s.lock.Lock()
    91  		now := time.Now()
    92  		for key, entry := range s.cache {
    93  			if now.After(entry.Timeout) {
    94  				delete(s.cache, key)
    95  			}
    96  		}
    97  		s.lock.Unlock()
    98  	}
    99  }