// 待存储的元素的结构体定义
typemyStructstruct{textstringmoreData[]byte}funcmain(){// 创建一个缓存表
cache:=cache2go.Cache("myCache")// 准备元素
val:=myStruct{"This is a test!",[]byte{}}// 缓存表添加元素,设定过期时间
cache.Add("someKey",5*time.Second,&val)// 从缓存中获取元素
res,err:=cache.Value("someKey")iferr==nil{fmt.Println("Found value in cache:",res.Data().(*myStruct).text)}else{fmt.Println("Error retrieving value from cache:",err)}// 等待直至元素过期
time.Sleep(6*time.Second)res,err=cache.Value("someKey")iferr!=nil{fmt.Println("Item is not cached (anymore).")}// 添加新的永不过期的元素
cache.Add("someKey",0,&val)// 设置删除元素时给反馈
cache.SetAboutToDeleteItemCallback(func(e*cache2go.CacheItem){fmt.Println("Deleting:",e.Key(),e.Data().(*myStruct).text,e.CreatedOn())})// 移除元素
cache.Delete("someKey")// 清空缓存
cache.Flush()}
typeCacheTablestruct{sync.RWMutex// 缓存表命名
namestring// 缓存表内存储的所有元素,是个slice
itemsmap[interface{}]*CacheItem// 触发清楚的定时器
cleanupTimer*time.Timer// 当前定时器间隔
cleanupIntervaltime.Durationlogger*log.Logger// Callback method triggered when trying to load a non-existing key.
loadDatafunc(keyinterface{},args...interface{})*CacheItem// Callback method triggered when adding a new item to the cache.
addedItem[]func(item*CacheItem)// Callback method triggered before deleting an item from the cache.
aboutToDeleteItem[]func(item*CacheItem)}
func(table*CacheTable)addInternal(item*CacheItem){// Careful: do not run this method unless the table-mutex is locked!
// It will unlock it for the caller before running the callbacks and checks
table.log("Adding item with key",item.key,"and lifespan of",item.lifeSpan,"to table",table.name)table.items[item.key]=item// Cache values so we don't keep blocking the mutex.
expDur:=table.cleanupIntervaladdedItem:=table.addedItemtable.Unlock()// Trigger callback after adding an item to cache.
ifaddedItem!=nil{for_,callback:=rangeaddedItem{callback(item)}}// If we haven't set up any expiration check timer or found a more imminent item.
ifitem.lifeSpan>0&&(expDur==0||item.lifeSpan<expDur){table.expirationCheck()}}
func(table*CacheTable)deleteInternal(keyinterface{})(*CacheItem,error){r,ok:=table.items[key]if!ok{returnnil,ErrKeyNotFound}// Cache value so we don't keep blocking the mutex.
aboutToDeleteItem:=table.aboutToDeleteItemtable.Unlock()// Trigger callbacks before deleting an item from cache.
ifaboutToDeleteItem!=nil{for_,callback:=rangeaboutToDeleteItem{callback(r)}}r.RLock()deferr.RUnlock()ifr.aboutToExpire!=nil{for_,callback:=ranger.aboutToExpire{callback(key)}}table.Lock()table.log("Deleting item with key",key,"created on",r.createdOn,"and hit",r.accessCount,"times from table",table.name)delete(table.items,key)returnr,nil}
func(table*CacheTable)expirationCheck(){table.Lock()iftable.cleanupTimer!=nil{table.cleanupTimer.Stop()}iftable.cleanupInterval>0{table.log("Expiration check triggered after",table.cleanupInterval,"for table",table.name)}else{table.log("Expiration check installed for table",table.name)}// To be more accurate with timers, we would need to update 'now' on every
// loop iteration. Not sure it's really efficient though.
now:=time.Now()smallestDuration:=0*time.Secondforkey,item:=rangetable.items{// Cache values so we don't keep blocking the mutex.
item.RLock()lifeSpan:=item.lifeSpanaccessedOn:=item.accessedOnitem.RUnlock()// 为0的元素永久保留
iflifeSpan==0{continue}// 通过元素的`accessedOn`属性判断是否过期,已过期则删除
ifnow.Sub(accessedOn)>=lifeSpan{// Item has excessed its lifespan.
table.deleteInternal(key)}else{// 未过期更新剩余生命时长
// Find the item chronologically closest to its end-of-lifespan.
ifsmallestDuration==0||lifeSpan-now.Sub(accessedOn)<smallestDuration{smallestDuration=lifeSpan-now.Sub(accessedOn)}}}// Setup the interval for the next cleanup run.
table.cleanupInterval=smallestDurationifsmallestDuration>0{table.cleanupTimer=time.AfterFunc(smallestDuration,func(){gotable.expirationCheck()})}table.Unlock()}