-
cache2go 介绍
开源的应用内缓存库,保证并发安全,提供 key-value 存储以及带过期时间控制。 key 与 value 可以是任意数据类型。 -
主要 API 说明
- Cache(): 返回已经存在的给定名称的缓存表,类型为 *CacheTable ,如果不存在,则创建一个新的缓存表。
- Add(): 向缓存表添加新的 key/value 对。可以同时指定该 key 的过期时间。如果为 0,则表示一直有效。
- Value(): 根据 key 返回缓存中对应的 value ,类型为 *CacheItem 。如果 key 存在,则同时更新它的访问计数和最后访问时间;如果不存在,则尝试调用事先注册的回调函数 loadData 初始化数据到缓存表。
- Delete(): 从缓存表中删除指定 key 。
- Exists(): 检查 key 是否存在于缓存中, 存在返回 true 。不会更新缓存项的最后访问时间
- Foreach(): 遍历缓存,对每个缓存项,调用自定义的回调函数进行处理。
- SetDataLoader(): 针对缓存设置数据加载回调函数,用于从缓存检索不存在的 key 时自动从其他地方如数据库、文件加载该 key/value 对到缓存中。
- SetAddedItemCallback(): 针对缓存设置新增 key/value 对到缓存时自动触发的回调函数。
- SetAboutToDeleteItemCallback(): 针对缓存设置删除 key 时自动触发的回调函数。
- SetAboutToExpireCallback(): 针对特定的 key 设置过期提醒回调函数。
- 源码分析
- 主要数据结构
// CacheTable 是一个带有名称的缓存表,通过名称标识可以支持同时创建多个不同用途的缓存。
type CacheTable struct {
// 内嵌读写互斥锁,用于保护本缓存表内部数据的并发访问
sync.RWMutex
// 缓存表的名称,名称不同表示不同的缓存表
name string
// 数据缓存项的实际存储位置,以 map 数据结构保存,支持任意类型的 key 和 value.
items map[interface{}]*CacheItem
// 负责执行过期缓存清理工作的定时器 Timer
cleanupTimer *time.Timer
// 下次缓存清理定时器执行前的等待时间,基于缓存中最早到期的时间计算出来
cleanupInterval time.Duration
// 用于本缓存表的日志记录器
logger *log.Logger
// 当检索一个不存在的 key 时自动执行的数据加载回调函数,主要用于缓存数据初始化
loadData func(key interface{}, args ...interface{}) *CacheItem
// 往缓存中增加一个新项时自动执行的回调函数
addedItem func(item *CacheItem)
// 从缓存中删除特定项之前自动执行的回调函数
aboutToDeleteItem func(item *CacheItem)
}
// CacheItem 表示缓存表中一个缓存项,其成员 data 即为缓存值。
type CacheItem struct {
// 内嵌读写互斥锁,用于保护本缓存项内部数据的并发访问
sync.RWMutex
// 缓存项的 key
key interface{}
// 缓存项的 value
data interface{}
// 有效时长/存活时长,本缓存项多久不被访问则过期,如果为 0,表示永不过期
lifeSpan time.Duration
// 本缓存项创建时间
createdOn time.Time
// 最后访问时间
accessedOn time.Time
// 已被访问次数
accessCount int64
// 本缓存项被删除之前自动执行的回调函数
aboutToExpire func(key interface{})
}
- 核心设计思想
-
通过设置不同名称支持同时创建多个缓存表 CacheTable ,每个缓存表包含一个 items 成员,使用 map 数据结构存放 key/value 对,其中 value 类型为 CacheItem 。 CacheItem 表示单个缓存项,内部包含了真正的缓存数据内容。通过 CacheItem 中记录的缓存有效时长、最后访问时间,实现缓存自动过期的功能。
-
缓存自动过期原理
每次往缓存中添加一个带过期时间控制的缓存项时,如果是第一次添加或者当前缓存项的过期时间比下次过期缓存清理定时器的执行时间还要早,则立即执行一次过期缓存清理(见 addInternal 和 expirationCheck 方法)。在 expirationCheck 方法中,会遍历缓存表,对于每个带过期时间控制的缓存项,执行以下其中一个操作:若当前时间减去最后访问时间已经超过有效时长,则删除该缓存;否则,计算出该缓存的剩余有效时长,通过比较找出整个缓存中最小的剩余有效时长,也就是下一个最先失效的缓存被删除的时间,也就是下次过期缓存清理定时器自动执行的时间。通过 Timer 自动在最小的剩余有效时长之后再次执行 expirationCheck 方法。