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 }