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

被折叠的 条评论
为什么被折叠?



