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

     1  package kmgCache
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/golang/groupcache/singleflight"
     8  )
     9  
    10  //请调用 NewTtlCache() 初始化
    11  type MemoryTtlCacheV2 struct {
    12  	cache       map[string]ttlCacheEntry
    13  	lock        sync.RWMutex
    14  	singleGroup singleflight.Group
    15  }
    16  
    17  // TODO 写一个close,以便可以关闭缓存
    18  func NewMemoryTtlCacheV2() *MemoryTtlCacheV2 {
    19  	c := &MemoryTtlCacheV2{
    20  		cache: map[string]ttlCacheEntry{},
    21  	}
    22  	go c.GcThread()
    23  	return c
    24  }
    25  
    26  // Ttl 内存缓存实现第二版
    27  // 1.使用key表示不同的项
    28  // 2.f 返回err 表示本次无法获取到信息,该err会被返回给调用者,并且此时调用者的value是nil
    29  // 3.使用singleGroup避免大量请求同时访问某个一个key
    30  func (s *MemoryTtlCacheV2) Do(key string, f func() (value interface{}, ttl time.Duration, err error)) (value interface{}, err error) {
    31  	entry, err := s.get(key)
    32  	if err == nil {
    33  		return entry.Value, nil
    34  	}
    35  	if err != CacheMiss {
    36  		return
    37  	}
    38  	entryi, err := s.singleGroup.Do(key, func() (interface{}, error) {
    39  		value, ttl, err := f()
    40  		return ttlCacheEntry{
    41  			Value:   value,
    42  			Timeout: time.Now().Add(ttl),
    43  		}, err
    44  	})
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	entry = entryi.(ttlCacheEntry)
    49  	s.save(key, entry)
    50  	return entry.Value, nil
    51  }
    52  
    53  func (s *MemoryTtlCacheV2) save(key string, entry ttlCacheEntry) {
    54  	s.lock.Lock()
    55  	defer s.lock.Unlock()
    56  	s.cache[key] = entry
    57  	return
    58  }
    59  
    60  func (s *MemoryTtlCacheV2) get(key string) (entry ttlCacheEntry, err error) {
    61  	s.lock.RLock()
    62  	defer s.lock.RUnlock()
    63  	now := time.Now()
    64  	entry, ok := s.cache[key]
    65  	if !ok {
    66  		return entry, CacheMiss
    67  	}
    68  	if now.After(entry.Timeout) {
    69  		return entry, CacheMiss
    70  	}
    71  	return entry, nil
    72  }
    73  
    74  //要有个进程在一边进行gc,避免内存泄漏
    75  func (s *MemoryTtlCacheV2) GcThread() {
    76  	for {
    77  		time.Sleep(time.Hour)
    78  		s.lock.Lock()
    79  		now := time.Now()
    80  		for key, entry := range s.cache {
    81  			if now.After(entry.Timeout) {
    82  				delete(s.cache, key)
    83  			}
    84  		}
    85  		s.lock.Unlock()
    86  	}
    87  }