分享
  1. 首页
  2. 文章

cache2go学习

themoonstone · · 2438 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

题外话:

不管学习什么语言,在基础掌握之后,阅读大牛写的开源项目确实是一种相当实在的方式(可是本人之前对此极为排斥,一直觉得要想NB还不如自己写项目,现在想来当时真是高看自己了!!),通过研究别人的代码,不仅能学习大牛们在代码用所用到的语言知识,还能学习他们的设计思想,看到更清晰的结构!这些都是实实在在的福利!因此,阅读开源项目在我看来是必不可少的,而为了防止自己看过遗忘,同时为了更好的理解,把自己学到的记下来,方便随时查看,修正!

cache2go

这是一个使用golang实现的并发安全并且包含超时机制的缓存库、缓存方式为表,表中可存储key-value方式的数据,篇幅所限,这里仅对其中的一些核心函数做说明

github地址:

https://github.com/muesli/cache2go

源码解析:

cache.go

该文件内容很少,只实现了一个功能:返回一张缓存表,如果不存在的话,进行创建

func Cache(table string) *CacheTable {
 mutex.RLock()
 t, ok := cache[table] //检查是否存在
 mutex.RUnlock()
 if !ok {
 mutex.Lock()
 t, ok = cache[table] //再次检查(没看懂这里为什么要进行二次检查)
 // Double check whether the table exists or not.
 if !ok {
 t = &CacheTable{
 name: table,
 items: make(map[interface{}]*CacheItem),
 }
 cache[table] = t
 }
 mutex.Unlock()
 }
 return t
}

cacheitem.go: 该文件主要实现缓存属性

type CacheItem struct {
 sync.RWMutex
 // The item's key.
 key interface{}
 // The item's data.
 data interface{}
 //在一直不访问该缓存的情况下,缓存存在的生命周期
 lifeSpan time.Duration
 // 缓存创建时间.
 createdOn time.Time
 //最后一次访问时间
 accessedOn time.Time
 //访问次数
 accessCount int64
 // 回调函数,删除缓存中的数据前触发
 aboutToExpire func(key interface{})
}

初始化cacheItem

func NewCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
 t := time.Now()
 return &CacheItem{
 key: key,
 lifeSpan: lifeSpan,
 createdOn: t,
 accessedOn: t,
 accessCount: 0,
 aboutToExpire: nil,
 data: data,
 }
}

更新访问时间和访问次数

func (item *CacheItem) KeepAlive() {
 item.Lock()
 defer item.Unlock()
 item.accessedOn = time.Now()
 item.accessCount++
}

设置aboutToExpire回调函数,可以在删除之前做自行一些处理,如备份等

func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
 item.Lock()
 defer item.Unlock()
 item.aboutToExpire = f
}

cachetable.go: cache实现的核心代码,对cache的CRUD操作都在这里

cache table

type CacheTable struct {
 sync.RWMutex
 // 表名.
 name string
 // 表中所有缓存数据.
 items map[interface{}]*CacheItem
 // 负责触发清理的定时器
 cleanupTimer *time.Timer
 // 当前定时器持续时间
 cleanupInterval time.Duration
 // 日志
 logger *log.Logger
 // 回调函数,当获取一个不存在的key时自动触发.
 loadData func(key interface{}, args ...interface{}) *CacheItem
 // 回调函数,当新增一个key时自动触发
 addedItem func(item *CacheItem)
 //回调函数,删除一个Key时自动触发.
 aboutToDeleteItem func(item *CacheItem)
}

cache大小

func (table *CacheTable) Count() int {
 table.RLock()
 defer table.RUnlock()
 return len(table.items)
}

遍历所有items,可在trans中对其进行处理

func (table *CacheTable) Foreach(trans func(key interface{}, item *CacheItem)) {
 table.RLock()
 defer table.RUnlock()
 for k, v := range table.items {
 trans(k, v)
 }
}

循环过期检查

func (table *CacheTable) expirationCheck() {
 table.Lock()
 // 停止清理定时器
 if table.cleanupTimer != nil {
 table.cleanupTimer.Stop()
 }
 if table.cleanupInterval > 0 {
 table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
 } else {
 table.log("Expiration check installed for table", table.name)
 }
 //实时更新当前时间
 now := time.Now()
 smallestDuration := 0 * time.Second
 for key, item := range table.items {
 item.RLock()
 lifeSpan := item.lifeSpan
 accessedOn := item.accessedOn
 item.RUnlock()
 if lifeSpan == 0 {
 continue
 }
 if now.Sub(accessedOn) >= lifeSpan {
 // 超出生命周期,直接删除
 table.deleteInternal(key)
 } else {
 // Find the item chronologically closest to its end-of-lifespan.(按时间顺序查找最接近其使用寿命的item,这里的条件感觉有点多余)
 if smallestDuration == 0 || lifeSpan-now.Sub(accessedOn) < smallestDuration {
 smallestDuration = lifeSpan - now.Sub(accessedOn)
 }
 }
 }
 // 设置下一次清理运行的间隔
 table.cleanupInterval = smallestDuration
 if smallestDuration > 0 {
 table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
 go table.expirationCheck() //递归调用
 })
 }
 table.Unlock()
}

向缓存中添加数据

func (table *CacheTable) addInternal(item *CacheItem) {
 //只能在加锁状态下调用 
 table.log("Adding item with key", item.key, "and lifespan of", item.lifeSpan, "to table", table.name)
 table.items[item.key] = item
 expDur := table.cleanupInterval
 addedItem := table.addedItem
 table.Unlock()
 // 触发addedItem回调函数
 if addedItem != nil {
 addedItem(item)
 }
 // 设置过期检查
 if item.lifeSpan > 0 && (expDur == 0 || item.lifeSpan < expDur) {
 table.expirationCheck()
 }
}

删除指定的Key-value

func (table *CacheTable) deleteInternal(key interface{}) (*CacheItem, error) {
 r, ok := table.items[key]
 if !ok {
 return nil, ErrKeyNotFound
 }
 aboutToDeleteItem := table.aboutToDeleteItem
 table.Unlock()
 // 触发aboutToDeleteItem函数
 if aboutToDeleteItem != nil {
 aboutToDeleteItem(r)
 }
 r.RLock()
 defer r.RUnlock()
 if r.aboutToExpire != nil {
 // 触发r.aboutToExpire函数
 r.aboutToExpire(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)
 return r, nil
}

判指定的key是否存在,不存在就添加

func (table *CacheTable) NotFoundAdd(key interface{}, lifeSpan time.Duration, data interface{}) bool {
 table.Lock()
 if _, ok := table.items[key]; ok {
 table.Unlock()
 return false
 }
 item := NewCacheItem(key, lifeSpan, data)
 table.addInternal(item)
 return true
}

获取指定缓存内容

func (table *CacheTable) Value(key interface{}, args ...interface{}) (*CacheItem, error) {
 table.RLock()
 r, ok := table.items[key]
 loadData := table.loadData
 table.RUnlock()
 if ok {
 // 更新访问时间和访问次数
 r.KeepAlive()
 return r, nil
 }
 // 如果不存在指定key,调用loadData
 if loadData != nil {
 item := loadData(key, args...)
 if item != nil {
 table.Add(key, item.lifeSpan, item.data)
 return item, nil
 }
 return nil, ErrKeyNotFoundOrLoadable
 }
 return nil, ErrKeyNotFound
}

清除缓存

func (table *CacheTable) Flush() {
 table.Lock()
 defer table.Unlock()
 table.log("Flushing table", table.name)
 table.items = make(map[interface{}]*CacheItem)
 table.cleanupInterval = 0
 if table.cleanupTimer != nil {
 table.cleanupTimer.Stop()
 }
}

每个Key的访问次数

type CacheItemPair struct {
 Key interface{}
 AccessCount int64 //访问次数
} 

返回访问量由多到少到排序列表

func (table *CacheTable) MostAccessed(count int64) []*CacheItem {
 table.RLock()
 defer table.RUnlock()
 p := make(CacheItemPairList, len(table.items))
 i := 0
 for k, v := range table.items {
 p[i] = CacheItemPair{k, v.accessCount}
 i++
 }
 sort.Sort(p) //降序排序
 var r []*CacheItem
 c := int64(0)
 for _, v := range p {
 if c >= count {
 break
 }
 item, ok := table.items[v.Key]
 if ok {
 r = append(r, item)
 }
 c++
 }
 return r
}

有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:themoonstone

查看原文:cache2go学习

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2438 次点击
1 回复 | 直到 2019年10月15日 17:35:54
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏