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 {
 
    }
}

 

 

posted @   -零  阅读(1959)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示