go key-value缓存go-cache实现
Cache类型
Cache封装了一个cache类型,cache类型的参数解析:
1.defaultExpiration time.Duration
每个键值的默认过期时间。
2.items map[string]Item
map类型。
3.mu sync.RWMutex
map类型的读写锁。
4.janitor *janitor
监控map中键值是否过期,定期删除map中过期的键值。
5.onEvicted func(string, interface{})
用户定义,可以对已经被删除键值做二次操作。
1 2 3 4 5 6 7 8 9 10 11 12 | type Cache struct { *cache // If this is confusing, see the comment at the bottom of New() } type cache struct { defaultExpiration time.Duration items map [string]Item mu sync.RWMutex onEvicted func (string, interface {}) janitor *janitor } |
1 | Item类型如下,定义 map 中key对应的每个值: |
1 2 3 4 5 | type Item struct { Object interface {} Expiration int64 //一个参数是Object存储value,另一个参数Expiration 为过期时间。 } |
初始化Cache
New(defaultExpiration, cleanupInterval time.Duration) *Cache {}:返回*Cache类型。
1 2 | //创建一个缓存库,这个缓存库默认每个键值的过期时间为五分钟,每十分钟清理一次 c := cache.New(5*time.Minute, 10*time.Minute) |
newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache{}:先创建map,继续往下调用。
1 2 3 4 | func New(defaultExpiration, cleanupInterval time.Duration) *Cache { items := make( map [string]Item) return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | func newCacheWithJanitor(de time.Duration, ci time.Duration, m map [string]Item) *Cache { //de为每个键值对的默认过期时间,ci为缓存池的定期扫描时间,m为缓存map:make(map[string]Item) //初始化cache c := newCache(de, m)<br> //初始化Cache C := &Cache{c} if ci > 0 { //监控map:创建监控器及定时器 runJanitor(c, ci) //给C绑定方法,当垃圾回收的时候执行 runtime.SetFinalizer(C, stopJanitor) } return C } |
newCache(de time.Duration, m map[string]Item) *cache :初始化cache:可以这里定义cache的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 | func newCache(de time.Duration, m map [string]Item) *cache { if de == 0 { de = -1 } c := &cache{ defaultExpiration: de, items: m, onEvicted: func (s string, i interface {}) { println( "键值被删除了,可以做这里做二次操做" ) }, } return c } |
监控器及定时器
监控器有两个属性,一个是定时扫描是否过期的时间,第二个为通知监控器关闭的信道。
1 2 3 4 | type janitor struct { Interval time.Duration stop chan bool } |
创建监控器及使用协程启动。
1 2 3 4 5 6 7 8 | func runJanitor(c *cache, ci time.Duration) { j := &janitor{ Interval: ci, stop: make( chan bool), } c.janitor = j go j.Run(c) } |
运行监控器,创建了一个定时器,使用select监控定时器信道和关闭信道。
1 2 3 4 5 6 7 8 9 10 11 12 13 | func (j *janitor) Run(c *cache) { //创建定时器 ticker := time.NewTicker(j.Interval) for { select { case <-ticker.C: //当定时器每次到达设置的时间时就会向管道发送消息,此时检查map中的键值是否过期 c.DeleteExpired() case <-j.stop: //监视器退出信道, ticker.Stop() return } } } |
添加记录
1 | c.Set( "foo" , "bar" , cache.DefaultExpiration) |
1 2 3 4 | const ( NoExpiration time.Duration = -1 DefaultExpiration time.Duration = 0 ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | func (c *cache) Set(k string, x interface {}, d time.Duration) { var e int64 if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 {<br> //这里很重要,如果设置缓存时间大于0,则在现在时间上加上设置的缓存时间 e = time.Now().Add(d).UnixNano() } c.mu.Lock() c.items[k] = Item{ Object: x, Expiration: e, } c.mu.Unlock() } |
删除记录
1 | c.Delete( "foo" ) |
1 2 3 4 5 6 7 8 | func (c *cache) Delete(k string) { c.mu.Lock()<br> //如果有OnEvicted方法,则返回k及true,如果没有,则返回空和false v, evicted := c.delete(k) c.mu.Unlock()<br> //evivted为真,则表示用户自定义了OnEvicted方法,对删的键值做删除后的操作 if evicted { c.onEvicted(k, v) } } |
1 2 3 4 5 6 7 8 9 10 11 12 | func (c *cache) delete(k string) ( interface {}, bool) { if c.onEvicted != nil { //如果存在OnEvicted方法,则执行, if v, found := c.items[k]; found { delete(c.items, k) return v.Object, true } } //如果不存在OnEvicted方法 delete(c.items, k) return nil, false } |
定期删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Delete all expired items from the cache. func (c *cache) DeleteExpired() { var evictedItems []keyAndValue //现在时间戳 now := time.Now().UnixNano() //map加互斥锁 c.mu.Lock() for k, v := range c.items { // "Inlining" of expired //检测map if v.Expiration > 0 && now > v.Expiration { //超时则删除 ov, evicted := c.delete(k) //err为真则会记录删除的键值,也表示onEvicted方法存在,对删除的key-value做二次操作 if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) } } } c.mu.Unlock() //解互斥锁 //c.onEvicted为函数类型,初始缓存map的时候赋值:func(string, interface{}) //替换这个函数:func (c *cache) OnEvicted(f func(string, interface{})) //可以对已经删除的key—value做二次操作,用户定义 for _, v := range evictedItems { c.onEvicted(v.key, v.value) } } |
go实现定时器模板:定时循环执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package main import "time" type janitor struct { Interval time.Duration stop chan bool } func (j *janitor) Run() { //创建定时器 ticker := time.NewTicker(j.Interval) for { select { case <-ticker.C: //当定时器每次到达设置的时间时就会向管道发送消息,此时检查map中的键值是否过期 print( "开始扫描\n" ) case <-j.stop: //监视器退出信道, ticker.Stop() close(j.stop) return } } } func stopJanitor(j *janitor) { j.stop <- true } func runJanitor(ci time.Duration) { j := &janitor{ Interval: ci, stop: make( chan bool), } go j.Run() } func main(){ runJanitor(time.Second) select { } } |
分类:
Go
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?