cache2go学习

题外话:

不管学习什么语言,在基础掌握之后,阅读大牛写的开源项目确实是一种相当实在的方式(可是本人之前对此极为排斥,一直觉得要想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
}

posted on 2017-09-30 11:31  残天  阅读(925)  评论(0)    收藏  举报

导航