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

     1  package kmgCache
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/golang/groupcache/singleflight"
     9  )
    10  
    11  var cacheExpire = errors.New("cache expire")
    12  var DoNotNeedCache = errors.New("do not need cache")
    13  
    14  //会在缓存超时的时候,异步更新缓存,并且返回前一个数据.
    15  type AsyncTtlCache struct {
    16  	cache       map[string]ttlCacheEntry
    17  	lock        sync.RWMutex
    18  	singleGroup singleflight.Group
    19  }
    20  
    21  func NewAsyncCache() *AsyncTtlCache {
    22  	return &AsyncTtlCache{
    23  		cache: map[string]ttlCacheEntry{},
    24  	}
    25  }
    26  
    27  //如果缓存不存在,会同步查询
    28  //如果缓存过期,会异步查询,以便下次请求的时候有这个数据
    29  // 如果你把needcache设置为false,表示这个请求本次不进行缓存(应该是什么地方出现错误了,具体错误请自行处理)
    30  // 1.如果缓存不存在,会同步查询
    31  // 2.如果缓存过去,会异步查询,并返回旧的数据
    32  // 3.如果不要求保存数据,不会把数据保存到缓存中.
    33  // 		1. 如果缓存不存在,返回f.value f.ttl
    34  func (s *AsyncTtlCache) DoWithTtl(key string, f func() (value interface{}, ttl uint32, canSave bool)) (value interface{}, ttl uint32) {
    35  	entry, err := s.get(key)
    36  	if err == nil {
    37  		return entry.Value, entry.GetTtl()
    38  	}
    39  	updateCache := func() (value interface{}, ttl uint32) {
    40  		//异步更新缓存
    41  		entryi, err := s.singleGroup.Do(key, func() (out interface{}, err error) {
    42  			value, ttl, canSave := f()
    43  			timeout := time.Now().Add(time.Duration(ttl) * time.Second)
    44  			out = ttlCacheEntry{
    45  				Value:   value,
    46  				Timeout: timeout,
    47  			}
    48  			if !canSave {
    49  				err = DoNotNeedCache
    50  			}
    51  			return
    52  		})
    53  		entryn := entryi.(ttlCacheEntry)
    54  		if err == nil {
    55  			s.save(key, entryn) //ttl 是0 也存进去,下次可以异步刷新
    56  		}
    57  		ttl = entryn.GetTtl()
    58  		return entryn.Value, ttl
    59  	}
    60  	switch err {
    61  	case CacheMiss:
    62  		value, ttl := updateCache()
    63  		return value, ttl
    64  	case cacheExpire:
    65  		go updateCache()
    66  		return entry.Value, 0
    67  	default:
    68  		return nil, 0
    69  	}
    70  }
    71  
    72  func (s *AsyncTtlCache) save(key string, entry ttlCacheEntry) {
    73  	s.lock.Lock()
    74  	defer s.lock.Unlock()
    75  	s.cache[key] = entry
    76  	return
    77  }
    78  
    79  func (s *AsyncTtlCache) get(key string) (entry ttlCacheEntry, err error) {
    80  	s.lock.RLock()
    81  	defer s.lock.RUnlock()
    82  	now := time.Now()
    83  	entry, ok := s.cache[key]
    84  	if !ok {
    85  		return entry, CacheMiss
    86  	}
    87  	if now.After(entry.Timeout) {
    88  		return entry, cacheExpire
    89  	}
    90  	return entry, nil
    91  }
    92  
    93  //要有个进程在一边进行gc,避免内存泄漏
    94  func (s *AsyncTtlCache) GcThread() {
    95  	for {
    96  		time.Sleep(time.Hour)
    97  		s.lock.Lock()
    98  		now := time.Now()
    99  		for key, entry := range s.cache {
   100  			if now.After(entry.Timeout) {
   101  				delete(s.cache, key)
   102  			}
   103  		}
   104  		s.lock.Unlock()
   105  	}
   106  }
   107  
   108  //里面数据的个数
   109  func (s *AsyncTtlCache) Len() int {
   110  	s.lock.RLock()
   111  	defer s.lock.RUnlock()
   112  	return len(s.cache)
   113  }