gin go 更新缓存_gin框架实践连载番外篇 | 3天打造专属Cache(First day)

本文介绍了如何使用Go语言的Gin框架构建一个灵活且高效的全局缓存管理器,支持多种缓存源。通过定义接口和适配器设计模式,实现了包括内存存储在内的缓存源注册和管理。文中提供了具体的代码示例,包括内存存储源的实现,以及如何使用这个缓存管理器进行操作,如设置、获取、删除缓存等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言Cache实际上在项目很常见、常见的redis、memcached等等

为了减少代码修改,灵活切换缓存存储源

今天开始为期3天交大家,如何打造一款高效扩展性强的的Cache管理器

主要用来学习啊、共同进步

1、gocache设计理念全局缓存管理器

支持多个缓存源

interface定义接口,完美实现适配器设计模式

2、定义定义全局存储源

定义全局Cache管理器var Stores = make(map[string]StoreInterface)type Cache struct {

name  string

store StoreInterface

}复制代码定义store接口type StoreInterface interface {

GetStoreName() string// 获取缓存

Get(key string) (interface{}, error)// 设置缓存带过期时间

Set(key string, value interface{}, time int) error// 设置永久缓存无过期时间

Forever(key string, value interface{}) error// 删除key

Delete(key string) error// 判断key是否存在

Has(key string) error// 全部清空

Clear() error// 获取缓存key的数量

Size() int// 获取expire

GetTTl(key string) (time.Duration, error)// 随机删除已过期key

GC()

}复制代码定义实例化Cache方法func New(name string) (*Cache, error) {

name = strings.ToLower(name)if store, ok := Stores[name]; ok {go store.GC()return &Cache{

name:  name, //有点多余

store: store,

}, nil

} else {return nil, fmt.Errorf("Cache:unknown %s store please import", name)

}

}复制代码定义注册缓存源的方法 (忽略store名称大小写,都转成小写存储)func Register(name string, store StoreInterface) {if store == nil {

log.Panic("Cache: Register store is nil")

}

name = strings.ToLower(name)if _, ok := Stores[name]; ok {

log.Panic("Cache: Register store is exist")

}

Stores[name] = store

}复制代码定义Cache对外方法// 获取store名称func (c *Cache) GetStoreName() string {return c.store.GetStoreName()

}// 获取键值func (c *Cache) Get(key string) (interface{}, error) {return c.store.Get(key)

}// 设置键值以及过期时间func (c *Cache) Set(key string, value interface{}, time int) error {return c.store.Set(key, value, time)

}// 永久设置键值不过期func (c *Cache) Forever(key string, value interface{}) error {return c.store.Forever(key, value)

}// 删除键值func (c *Cache) Delete(key string) error {return c.store.Delete(key)

}// 判断键是否存在func (c *Cache) Has(key string) error {return c.store.Has(key)

}// 清除所有键值func (c *Cache) Clear() error {return c.store.Clear()

}func (c *Cache) Size() int {return c.store.Size()

}func (c *Cache) GetTTl(key string) (time.Duration, error) {return c.store.GetTTl(key)

}复制代码

3、实现一个内存存储源 github地址新建一个store目录,新建memory.go

定义 MemoryStoretype MemoryStore struct {

list       map[string]*list.Element  //真实存储

expireList *list.List //要过期的键(双向链表)

mu         sync.RWMutex 读写锁保证并发读写

}type Memory struct {

key     string

value   interface{}

expirat time.Time

}var MemoryName = "Memory" //store的名称复制代码实例化store以及初始化注册cachefunc init() {

gocache.Register(MemoryName, NewStore())

}func NewStore() *MemoryStore {return &MemoryStore{

list:       make(map[string]*list.Element),

expireList: list.New(),

}

}复制代码Memory实现的方法func (m *Memory) Get() interface{} {return m.value

}func (m *Memory) Set(key string, value interface{}, d int) {

m.key = key

m.value = value

m.expirat = time.Now().Add(time.Duration(d) * time.Second)

}func (m *Memory) Forever(key string, value interface{}) {

m.key = key

m.value = value

}func (m *Memory) TTL(key string) time.Time {return m.expirat

}复制代码store实现的方法func (ms *MemoryStore) GetStoreName() string {return MemoryName

}func (ms *MemoryStore) Get(key string) (interface{}, error) {defer ms.mu.RUnlock()

ms.mu.RLock()

isExpre, err := ms.IsExpire(key)if err != nil {return nil, err

} else {if isExpre {

ms.expireList.Remove(ms.list[key])delete(ms.list, key)return nil, fmt.Errorf("Cache :%s not found", key)

} else {return ms.list[key].Value.(*Memory).Get(), nil

}

}

}func (ms *MemoryStore) Set(key string, value interface{}, time int) error {if value == nil {return fmt.Errorf("Cache : %s set value not is nil", "Memory")

}

ms.mu.Lock()defer ms.mu.Unlock()

m := &Memory{}

m.Set(key, value, time)

element := ms.expireList.PushBack(m)

ms.list[key] = elementreturn nil}func (ms *MemoryStore) Delete(key string) error {defer ms.mu.Unlock()

ms.mu.Lock()if _, ok := ms.list[key]; !ok {return fmt.Errorf("Cache :%s value not found", key)

} else {

ms.expireList.Remove(ms.list[key])delete(ms.list, key)return nil

}

}func (ms *MemoryStore) Has(key string) error {defer ms.mu.RUnlock()

ms.mu.RLock()if _, ok := ms.list[key]; ok {return nil

} else {return fmt.Errorf("Cache :%s value not found", key)

}

}func (ms *MemoryStore) Forever(key string, value interface{}) error {defer ms.mu.Unlock()

ms.mu.Lock()if value == nil {return fmt.Errorf("Cache : %s set value not is nil", "Memory")

}

m := &Memory{}

m.Forever(key, value)//无过期时间的只存在list map

list := list.New()

element := list.PushBack(m)

ms.list[key] = elementreturn nil}func (ms *MemoryStore) Clear() error {defer ms.mu.Unlock()

ms.mu.Lock()

ms.list = make(map[string]*list.Element)

ms.expireList = list.New()return nil}func (ms *MemoryStore) Size() int {defer ms.mu.RUnlock()

ms.mu.RLock()return len(ms.list)

}func (ms *MemoryStore) GetTTl(key string) (time.Duration, error) {defer ms.mu.RUnlock()

ms.mu.RLock()if err := ms.Has(key); err != nil {return time.Duration(0), err

} else {return time.Now().Sub(ms.list[key].Value.(*Memory).TTL(key)), nil

}

}func (ms *MemoryStore) IsExpire(key string) (bool, error) {if err := ms.Has(key); err != nil {return false, err

}

expire := ms.list[key].Value.(*Memory).TTL(key)return !expire.IsZero() && time.Now().After(expire), nil}复制代码重点说下过期策略参考redis实现了这个惰性删除和定时删除

定时删除主要是用到了go 的定时器 每隔2秒去扫expireList (更优的方案,可以考虑用堆实现,堆顶是过期时间戳最小的)func (ms *MemoryStore) GC() {

ticker := time.NewTicker(2 * time.Second)for {

fmt.Println("GC Start")select {case 

elememt := ms.expireList.Back()if elememt == nil {break

}

key := elememt.Value.(*Memory).keyif isbool, _ := ms.IsExpire(key); isbool {

ms.expireList.Remove(elememt)delete(ms.list, key)

fmt.Println("缓存删除成功:", key)

}

}

}

}复制代码

4、demo走起package mainimport ("fmt""log""time""github.com/18211167516/gocache"

_ "github.com/18211167516/gocache/store")func main() {

cache, err := gocache.New("memory")if err != nil {

log.Fatalf("初始化缓存管理器 失败 %s", err)

}

fmt.Println("获取store type:", cache.GetStoreName())

fmt.Println(time.Now())

fmt.Println("删除全部:", fmt.Sprintln(cache.Clear()))if err := cache.Set("aaaa", "123123", 100); err != nil {

log.Fatalf("设置键 %s 的value 失败 %s", "aaaa", err)

}

fmt.Println("判断键值:", fmt.Sprintln(cache.Has("aaaa")))

fmt.Println("获取键值:", fmt.Sprintln(cache.Get("aaaa")))

fmt.Println("获取ttl:", fmt.Sprintln(cache.GetTTl("aaaa")))/* fmt.Println("设置永久键值:", cache.Forever("bbb", "bbbb"))

fmt.Println("获取ttl:", fmt.Sprintln(cache.GetTTl("bbb")))

fmt.Println("获取键值:", fmt.Sprintln(cache.Get("bbb"))) */

_ = cache.Set("cccc", "cccc", 5)

_ = cache.Set("dddd", "dddd", 20)

fmt.Println("获取键值:", fmt.Sprintln(cache.Get("cccc")))

time.Sleep(time.Duration(5) * time.Second)

fmt.Println("延迟5秒获取键值:", fmt.Sprintln(cache.Get("cccc")))

fmt.Println("获取键值:", fmt.Sprintln(cache.Get("dddd")))

fmt.Println("获取ttl:", fmt.Sprintln(cache.GetTTl("dddd")))//fmt.Println("删除全部:", fmt.Sprintln(cache.Clear()))

fmt.Println("缓存个数:", cache.Size())//select {}

ticker := time.NewTicker(5 * time.Second)

exit:for {select {case 

size := cache.Size()

fmt.Println("缓存个数:", size)if size == 0 {

ticker.Stop()break exit

}

}

}

}复制代码

5、结尾定义interface是golang扩展性支持的关键

实际上就内存存储源也可以inteface话来用多个数据结构实现

6、其它与诸君共勉之,写的不好的地址给指正,希望对您有帮助,思路很重要

7、系列文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值