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 }